I have an ASP.net core web API with swagger (using swashbuckle).
One of the actions in the Web API is a file upload action:
[Produces("application/json")]
[Route("[controller]")]
public class FilesController : Controller
{
[HttpPost]
public void Post(IFormFile file)
{
...
}
}
When I look up that action in the swagger UI it let's me fill in all the fields of IFormFile
, which is not what I want to do to test my API.
So how can I add an upload button to the Swagger UI?
First add a operation filter that consumes the multipart formdata.
public class FileUploadOperation : IOperationFilter
{
private readonly IEnumerable<string> _actionsWithUpload = new []
{
//add your upload actions here!
NamingHelpers.GetOperationId<FilesController>(nameof(FilesController.Post))
};
public void Apply(Operation operation, OperationFilterContext context)
{
if (_actionsWithUpload.Contains(operation.OperationId) )
{
operation.Parameters.Clear();
operation.Parameters.Add(new NonBodyParameter
{
Name = "file",
In = "formData",
Description = "Upload File",
Required = true,
Type = "file"
});
operation.Consumes.Add("multipart/form-data");
}
}
}
/// <summary>
/// Refatoring friendly helper to get names of controllers and operation ids
/// </summary>
public class NamingHelpers
{
public static string GetOperationId<T>(string actionName) where T : Controller => $"{GetControllerName<T>()}{actionName}";
public static string GetControllerName<T>() where T : Controller => typeof(T).Name.Replace(nameof(Controller), string.Empty);
}
Now you should add your actions to the _actionWithUpload
array!
Note that I added the extesnions only to have a refactoring friendly filter.
Last but not least, make sure the operation filter is added to the options of swagger. So add options.OperationFilter<FileUploadOperation>();
to your swagger options and done.
Full example:
services.AddSwaggerGen(options =>
{
options.SwaggerDoc(Version, new Info
{
Title = Title,
Version = Version
}
);
var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, $"{_webApiAssemblyName}.xml");
options.IncludeXmlComments(filePath);
options.DescribeAllEnumsAsStrings();
//this is the step where we add the operation filter
options.OperationFilter<FileUploadOperation>();
});
In addition to @Nick's answer, I have to make 2 changes for AspNet core 2.
1] Updated GetOperationId()
Now all operationIds contain API prefix as well as Method in the suffix. So I have statically added API & Post in ActionName.
public static string GetOperationId<T>(string actionName) where T : ControllerBase => $"Api{GetControllerName<T>()}{actionName}Post";
2] Remove only file parameter
Instead of removing all parameters for that action, I want to remove only the file parameter.
var fileParameter = operation.Parameters.FirstOrDefault(x => x.Name == "file" && x.In == "body");
if (fileParameter != null)
{
operation.Parameters.Remove(fileParameter);
...
}