
Handling file upload in ASP.NET Core 5 with Swagger UI
Generating proper file upload in Swagger UI for file upload in ASP.NET Core 5
In 2019 I worked on a REST API service which accepts files and stores them. Back then in .NET Core 2.2 I wrote custom Swagger operation filter which I described in article Handling file uploads in Swagger UI with ASP.NET Core. Since then two new major releases of .NET Core were published, 3.0 and 5.0 and it was the time to upgrade the service to latest 5.0 version of ASP.NET Core.
However there were some obsolete methods which were no longer supported in new framework so they had to be rewritten. One among these was Swagger operation filter responsible for proper file upload representation in Swagger UI.
In case you do not want to go through the old, obsolete article from 2019 I mentioned above, I will briefly describe the the problem here and finally provide new operation filter for latest Swagger UI for .NET Core 5.0.
The problem
To describe the problem I used a simple sample web api with one controller and with only one controller method only for uploading the file
[ApiController] [Route("[controller]")] public class DocumentsController : ControllerBase { [HttpPost] public async Task<IActionResult> UploadDocument( [FromHeader] String documentType, [FromForm] IFormFile file ) { // TODO: handle file upload return await Task.FromResult(Ok()); } }
This is how you would typically add file as a parameter for upload to an API endpoint in ASP.NET Core controller method. However, when you run this from your IDE you will get the following UI representation in Swagger UI.
As you can see, file upload is not properly represented in the Swagger UI which makes testing of this endpoint pretty much impossible directly from the Swagger UI directly and in order to test it you would have to use POSTMAN or CURL.
Ideally we should have a file upload control here so we can pick the file from he local machine and test the endpoint. This is where custom Swagger operation filter comes into play. We'll intercept SwaggerGen OAS generation process via our custom IOperationFilter implementation to describe this endpoint properly and render out the proper UI.
Swagger file upload operation filter
If you look at this endpoint and potentially any endpoint you may have for uploading the files in your Web API, you will notice that at least one of the parameters, specifically the one that carries the file content is of type IFormFile. We can use this to identify the file upload methods and alter them.
After we apply this condition in our filter, we just need to alter the operation parameter schema properties with the new ones that indicate the binary content.
public class SwaggerFileOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { var fileUploadMime = "multipart/form-data"; if (operation.RequestBody == null || !operation.RequestBody.Content.Any(x => x.Key.Equals(fileUploadMime, StringComparison.InvariantCultureIgnoreCase))) return; var fileParams = context.MethodInfo.GetParameters().Where(p => p.ParameterType == typeof(IFormFile)); operation.RequestBody.Content[fileUploadMime].Schema.Properties = fileParams.ToDictionary(k => k.Name, v => new OpenApiSchema() { Type = "string", Format = "binary" }); } }
The only thing left is to plug in this operation class to our Web API DI container registration in Startup.cs
public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "Sample.FileUpload.Api", Version = "v1" }); c.OperationFilter<SwaggerFileOperationFilter>(); }); }
Now once we start the service and access the Swagger UI, we'll get the proper UI and you can select the file from local machine and test the endpoint directly from Swagger UI.
References
- Handling file uploads in Swagger UI with ASP.NET Core
- Get started with Swashbuckle and ASP.NET Core
- ASP.NET Core web API documentation with Swagger / OpenAPI
- Swashbuckle.AspNetCore
Disclaimer
Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.
Comments for this article