Adding a File Upload Field to Your Swagger UI With Swashbuckle

If you’re building ASP.NET Core Web APIs, then I hope you’ve heard of Swashbuckle – the tool to generate the Swagger UI automatically for all of your controllers to make manual testing your endpoints visual and simple.

Out of the box, the documentation helps you set up your UI, handle different ways to authenticate (which we will touch on in a later post), and have it all hooked up to your controllers. However, this only handles the most common cases of required requests with query string parameters and HTTP Body content.

In this post, we’ll look at a quick and easy way to also add File upload fields for your API endpoints that consume IFormFile properties to make testing file uploading even easier.

Basic Swagger Setup

First thing’s first, install that puppy:

Package Manager : Install-Package Swashbuckle.AspNetCore
CLI : dotnet add package Swashbuckle.AspNetCore

Let’s first look at a simple swagger setup as our baseline before we add everything for our HTTP Header Field.

Startup.cs

//...
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSwaggerGen(config =>
    {
        config.SwaggerDoc("v1", new Info { Title = "My API", Version = "V1" });
    });
    // ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...
    app.UseMvc();

    app.UseSwagger();
    app.UseSwaggerUI(config =>
    {
        config.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1");
    });
    // ...
}
//...

This setup gives us all we need for our basic UI and wireup to our controllers!
Running this gives us our basic swagger at /swagger:
Swagger no header

Adding a File Upload Field

What we have to do now is add an OperationFilter to our swagger generation. These OperationFilters can do a whole lot and enable us to customize the swagger document created which is what drives the fields and info on the UI.

Let’s start by creating our FormFileSwaggerFilter class.

FormFileSwaggerFilter.cs

/// <summary>
/// Filter to enable handling file upload in swagger
/// </summary>
public class FormFileSwaggerFilter : IOperationFilter
{
    private const string formDataMimeType = "multipart/form-data";
    private static readonly string[] formFilePropertyNames =
        typeof(IFormFile).GetTypeInfo().DeclaredProperties.Select(p => p.Name).ToArray();

    public void Apply(Operation operation, OperationFilterContext context)
    {
        var parameters = operation.Parameters;
        if (parameters == null || parameters.Count == 0) return;

        var formFileParameterNames = new List<string>();
        var formFileSubParameterNames = new List<string>();

        foreach (var actionParameter in context.ApiDescription.ActionDescriptor.Parameters)
        {
            var properties =
                actionParameter.ParameterType.GetProperties()
                    .Where(p => p.PropertyType == typeof(IFormFile))
                    .Select(p => p.Name)
                    .ToArray();

            if (properties.Length != 0)
            {
                formFileParameterNames.AddRange(properties);
                formFileSubParameterNames.AddRange(properties);
                continue;
            }

            if (actionParameter.ParameterType != typeof(IFormFile)) continue;
            formFileParameterNames.Add(actionParameter.Name);
        }

        if (!formFileParameterNames.Any()) return;

        var consumes = operation.Consumes;
        consumes.Clear();
        consumes.Add(formDataMimeType);

        foreach (var parameter in parameters.ToArray())
        {
            if (!(parameter is NonBodyParameter) || parameter.In != "formData") continue;

            if (formFileSubParameterNames.Any(p => parameter.Name.StartsWith(p + "."))
                || formFilePropertyNames.Contains(parameter.Name))
                parameters.Remove(parameter);
        }

        foreach (var formFileParameter in formFileParameterNames)
        {
            parameters.Add(new NonBodyParameter()
            {
                Name = formFileParameter,
                Type = "file",
                In = "formData"
            });
        }
    }
}

This operation filter takes the operation parameters, then uses reflection to find the type of the field. If the field is an IFormFile, then we add a new file field from the formData section to our parameters. This in turn will update our swagger definition json file, and when rendered adds the field to our UI.

This even works great with endpoints that take a separate HTTP Body, query parameters, and files!

Now we need to reference it in our Startup when we initialize swagger:

Startup.cs

//...
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSwaggerGen(config =>
    {
        config.SwaggerDoc("v1", new Info { Title = "My API", Version = "V1" });

        config.OperationFilter<FormFileSwaggerFilter>();
    });
    // ...
}

Here’s an example controller with an endpoint that uses the file upload:

FileUploadController

[Route("api/[controller]")]
public class FileUploadController : Controller
{
    [HttpPost]
    public async Task<IActionResult> CreateMediaItem(string name, [FromForm]IFormFile file)
    {
        // Do something with the file
        return Ok();
    }
}

This controller ends up rendering in our Swagger UI as:
Fileupload_swagger

And using this, we can now submit our request with an uploaded file and all our other parameters!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Advertisements

Adding a Required HTTP Header to Your Swagger UI With Swashbuckle

If you’re building ASP.NET Core Web APIs, then I hope you’ve heard of Swashbuckle – the tool to generate the Swagger UI automatically for all of your controllers to make manual testing your endpoints visual and simple.

Out of the box, the documentation helps you set up your UI, handle different ways to authenticate (which we will touch on in a later post), and have it all hooked up to your controllers. However, this only handles the most common cases of required requests with query string parameters and HTTP Body content.

In this post, we’ll look at a quick and easy way to also add fields for your custom HTTP Request Headers so you can fill them out while testing without having to do some funky stuff in the console.

Basic Swagger Setup

First thing’s first, install that puppy:

Package Manager : Install-Package Swashbuckle.AspNetCore
CLI : dotnet add package Swashbuckle.AspNetCore

Let’s first look at a simple swagger setup as our baseline before we add everything for our HTTP Header Field.

Startup.cs

//...
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSwaggerGen(config =>
    {
        config.SwaggerDoc("v1", new Info { Title = "My API", Version = "V1" });
    });
    // ...
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    // ...
    app.UseMvc();

    app.UseSwagger();
    app.UseSwaggerUI(config =>
    {
        config.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1");
    });
    // ...
}
//...

This setup gives us all we need for our basic UI and wireup to our controllers!
Running this gives us our basic swagger at /swagger:
Swagger no header

Adding Custom Headers

What we have to do now is add an OperationFilter to our swagger generation. These OperationFilters can do a whole lot and enable us to customize the swagger document created which is what drives the fields and info on the UI.

Let’s create a MyHeaderFilter and then add it to the AddSwaggerGen call.

MyHeaderFilter.cs

/// <summary>
/// Operation filter to add the requirement of the custom header
/// </summary>
public class MyHeaderFilter : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        if (operation.Parameters == null)
            operation.Parameters = new List<IParameter>();

        operation.Parameters.Add(new NonBodyParameter
        {
            Name = "MY-HEADER",
            In = "header",
            Type = "string",
            Required = true // set to false if this is optional
        });
    }
}

And now we need to update our Startup class:

Startup.cs

//...
public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddSwaggerGen(config =>
    {
        config.SwaggerDoc("v1", new Info { Title = "My API", Version = "V1" });

        config.OperationFilter<MyHeaderFilter>();
    });
    // ...
}

Now when we run and navigate to our /swagger url, we can see:
Swagger with header

When we fill out that field, we can now pull the value off the request header in our controller!

ContentController.cs

[Route("api/[controller]")]
public class ContentController : Controller
{
    public void Search([FromBody]InputModel model)
    {
        Console.WriteLine(Request.Headers["MY-HEADER"]);        
    }
}

In further posts, we’ll talk about adding Bearer Authentication, XML Comments, and More 🙂


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

Add Model Validation to Your ASP.NET Core Web API

If you’re used to creating traditional Web APIs in ASP.NET, you’ve probably become accustom to using System.ComponentModel.DataAnnotations such as the [Required] or [StringLength] data attributes. Using these in your models that are parameters to your API project would return the proper 400 response code when the input didn’t match the requirement.

In ASP.NET Core 2.0, this is no longer built in right out of the box, but setting it up is super easy! So if you don’t want to abstract your model validation in your business logic layer and want to get it out of the way right away when the request comes in, check this out.

There are only two steps to doing this:

  • Create an implementation of `IActionFilter`
  • Register this Filter class in your startup

Let’s start by creating a ValidatorActionFilter class:

ValidatorActionFilter.cs

/// <summary>
/// Filter for returning a result if the given model to a controller does not pass validation
/// </summary>
public class ValidatorActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!filterContext.ModelState.IsValid)
        {
            filterContext.Result = new BadRequestObjectResult(filterContext.ModelState);
        }
    }

    public void OnActionExecuted(ActionExecutedContext filterContext)
    {

    }
}

What this does is check the current HttpContext.ModelState to make sure it is valid. This allows us to ensure we don’t have to make this check in each endpoint. Note, we don’t have to add anything in the OnActionExecuted method since this filter is used exclusively before the action is called.

In the OnActionExecuting call, if the model state is NOT valid, we need to return the 400 – Bad Request response as the result.

Now, let’s register this in our startup. We do this by applying it in the ConfigureServices call and specifically in the options of the AddMvc call:

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(typeof(ValidatorActionFilter));
    });
}

And that’s it! So now your endpoints will return the proper response such as:

MyInputModel.cs

public class MyInputModel
{
    [Required]
    public string SomeRequiredValue { get; set; }
    public string SomeNotRequiredValue { get; set; }
}

MyController.cs

public class MyController : Controller
{
    public ActionResult DoSomeAction([FromBody]MyInputModel input)
    {
         return Ok("You did it"!)
    }
}

The request of:

{
    "someNotRequiredValue": "Hey"
}

Will return 400 - Bad Request - "The SomeRequiredValue field is required"

And the request of:

{
    "someRequiredValue": "Yo"
    "someNotRequiredValue": "Hey"
}

Will return 200 - Ok - "You did it!"

So go forth and validate those input models with ease!


If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!

Interested in sponsoring developer content? Message @Suave_Pirate on twitter for details.

ASP.NET.Tips – Consuming Bearer Tokens as a Cookie

A while ago, I talked about creating a super basic OAuth Bearer and Refresh Token System in your ASP.NET web applications: Adding a Simple Refresh Token to OAuth Bearer Tokens

Now, almost two years later, we will expand on this by creating a Cookie provider that consumes your bearer tokens to make Authorization easier. One reason to consider doing this is if you are using SignalR or any other socket service with your OAuth tokens. You can add your Bearer token in your Authorization header of your requests to SignalR, however, doing this will force your client to use LongPolling rather than actually using WebSockets as it is intended.

So, let’s create our provider:

OAuthCookieProvider.cs

 public class OAuthCookieProvider : OAuthBearerAuthenticationProvider
    {
        public override Task RequestToken(OAuthRequestTokenContext context)
        {
            if (context == null) throw new ArgumentException("context");
            var tokenCookie = context.OwinContext.Request.Cookies["BearerToken"];
            if (!string.IsNullOrEmpty(tokenCookie))
            {
                context.Token = tokenCookie;
                return Task.FromResult<object>(null);
            }
            return base.RequestToken(context);
        }
    }

And now let’s get that registered with Owin:

Startup.cs

...
public void Configuration(IAppBuilder app)
{
    app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions
    {
        Provider = new OAuthCookieProvider()
    });
}
...

Now we can send requests with our HTTP cookie with the key of BearerToken and make it through the built in Authorize attribute without having to write anything custom.

Next, we will look at taking advantage of this CookieProvider in a .NET Signalr Client to use the full power and speed of web socket connections.

If you like what you see, don’t forget to follow me on twitter @Suave_Pirate, check out my GitHub, and subscribe to my blog to learn more mobile developer tips and tricks!