I have an ASP.NET Core 2.2 WebApi and want to upload large files with some additional metadata. The request is a multipart/form-data. Because the files to upload can get quite large, I do not want to read it into memory for processing but rather stream it directly to it's desired destination. I followed the documentation to disable form value model binding and I also adjusted the maximum request size for the endpoint.
I have tested the endpoint with postman and it works as expected:

However, Swagger obviously does not recognize that there should be parameters for the request. How can I add these parameters to the swagger documentation without defining the parameters in the method's signature?
My endpoint looks like the following example:
[HttpPost]
[DisableFormValueModelBinding]
[DisableRequestSizeLimit]
public async Task<IActionResult> Upload() // "department" and "file" needed in the multipart/form-data
{
// var path = await uploader.UploadAsync(Request);
// return Ok(path);
}
Usually, I would bind the parameters like the following example:
public async Task<IActionResult> Upload([FromForm] string department, [FromForm] IFormFile file)
This works as expected in Swagger but as mentioned above, I do not want to bind the parameters.
For Swashbuckle.AspNetCore version 5 and above some things have changed.
To provide the parameters like Alexander did in his answer, the code would look something like the following:
operation.Parameters.Add(new OpenApiParameter()
{
Name = "department",
Schema = new OpenApiSchema { Type = "string", Format = "string" },
Required = true,
});
operation.Parameters.Add(new OpenApiParameter()
{
Name = "file",
Schema = new OpenApiSchema { Type = "string", Format = "binary" },
Required = true,
});
For some reason however (which I did not investigate further), I was not able to perform an call in the Swagger UI with this approach.
In the end, the following example provided me the result I was looking for:
public class AddUnboundParametersOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var descriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
if (descriptor != null && descriptor.ControllerTypeInfo == typeof(RemoteUpdateController) && descriptor.ActionName == nameof(RemoteUpdateController.Upload))
{
var openApiMediaType = new OpenApiMediaType
{
Schema = new OpenApiSchema
{
Type = "object",
Required = new HashSet<string> { "department", "file" }, // make the parameter(s) required if needed
Properties = new Dictionary<string, OpenApiSchema>
{
{ "department" , new OpenApiSchema() { Type = "string", Format = "string" } },
{ "file" , new OpenApiSchema() { Type = "string", Format = "binary" } },
}
}
};
operation.RequestBody = new OpenApiRequestBody
{
Content = new Dictionary<string, OpenApiMediaType>
{
{ "multipart/form-data", openApiMediaType }
}
};
}
}
}
You can use IOperationFilter for this. Add the following class, adjust controller and action names
public class AddUnboundParametersOperationFilter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
var descriptor = context.ApiDescription.ActionDescriptor as ControllerActionDescriptor;
if (descriptor != null && descriptor.ControllerTypeInfo == typeof(TestController) && descriptor.ActionName == nameof(TestController.Upload))
{
operation.Parameters.Add(new NonBodyParameter()
{
Name = "department",
Type = "string",
Required = true,
In = "formData",
});
operation.Parameters.Add(new NonBodyParameter()
{
Type = "file",
In = "formData",
Name = "file",
Required = true
});
}
}
}
In Startup.cs
services.AddSwaggerGen(c =>
{
c.OperationFilter<AddUnboundParametersOperationFilter>();
//...
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With