Asp.Net Core Swagger man pages for IFormFile

I am trying to configure swagger to test models that have IFormFile properties. For example, I have the following API method

[HttpPost]
public ApiResult<UserModel> SaveTestFileData([FromForm]TestPostFileArgs args)
{
    var result = new UserModel() { Id = 1, Name = $"SaveTestFileData {args.UserId} company: {args.CompanyId}, file length: {args.CompanyFile.Length}" };
    return ApiResult.Success(result);
}

And my parameter model

public class TestPostFileArgs
{
    public int UserId { get; set; }
    public int? CompanyId { get; set; }
    public IFormFile CompanyFile { get; set; }
}

By default, swagger generates a help page that does not allow it to be tested. Wrong swagger uiTo solve this problem, I wrote the following OperationFilter

public class FormFileOperationFilter: IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        if (operation.Parameters == null)
            return;

        var fileParamNames = context.ApiDescription.ActionDescriptor.Parameters
            .SelectMany(x => x.ParameterType.GetProperties())
            .Where(x => x.PropertyType.IsAssignableFrom(typeof (IFormFile)))
            .Select(x => x.Name)
            .ToList();
        if (!fileParamNames.Any())
            return;

        var paramsToRemove = new List<IParameter>();
        foreach (var param in operation.Parameters)
        {
            paramsToRemove.AddRange(from fileParamName in fileParamNames where param.Name.StartsWith(fileParamName + ".") select param);
        }
        paramsToRemove.ForEach(x => operation.Parameters.Remove(x));
        foreach (var paramName in fileParamNames)
        {
            var fileParam = new NonBodyParameter
                {
                    Type = "file",
                    Name = paramName,
                    In = "formData"
                };
            operation.Parameters.Add(fileParam);
        }
        foreach (IParameter param in operation.Parameters)
        {
            param.In = "formData";
        }

        operation.Consumes = new List<string>() { "multipart/form-data" };
    }
}

And after that, everything works as I expect from swagger. Correct swagger ui

At the moment, this solution works for me, but it seems not right. Maybe I am missing a simple solution for this. Also, this approach does not handle List properties or complex objects using an IFormFile or possibly something else.

+6
3

ASP.NET Core , Swashbuckle.AspNetCore GitHub : https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/193. OperationFilter - , .

+3

, .

OperationFilter, IFormFile , .

public class AddFileUploadParams : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        if (operation.Parameters == null)
            return;

        var formFileParams = context.ApiDescription.ActionDescriptor.Parameters
                                .Where(x => x.ParameterType.IsAssignableFrom(typeof(IFormFile)))
                                .Select(x => x.Name)
                                .ToList(); ;

        var formFileSubParams = context.ApiDescription.ActionDescriptor.Parameters
            .SelectMany(x => x.ParameterType.GetProperties())
            .Where(x => x.PropertyType.IsAssignableFrom(typeof(IFormFile)))
            .Select(x => x.Name)
            .ToList();

        var allFileParamNames = formFileParams.Union(formFileSubParams);

        if (!allFileParamNames.Any())
            return;

        var paramsToRemove = new List<IParameter>();
        foreach (var param in operation.Parameters)
        {
            paramsToRemove.AddRange(from fileParamName in allFileParamNames where param.Name.StartsWith(fileParamName + ".") select param);
        }
        paramsToRemove.ForEach(x => operation.Parameters.Remove(x));
        foreach (var paramName in allFileParamNames)
        {
            var fileParam = new NonBodyParameter
            {
                Type = "file",
                Name = paramName,
                In = "formData"
            };
            operation.Parameters.Add(fileParam);
        }
        foreach (IParameter param in operation.Parameters)
        {
            param.In = "formData";
        }

        operation.Consumes = new List<string>() { "multipart/form-data" };
    }
}

, SwashBuckle, . , swashBuckle . , , .

+2

   [Route("picture/pics/upload/")]

UploadPicture(IFormFile file)

    [Route("picture/{Id:int:min(1)}/{sequence:int:min(1)}")]
    [SwaggerResponse((int)HttpStatusCode.NotFound)]
    [SwaggerResponse((int)HttpStatusCode.BadRequest)]
    [SwaggerResponse((int)HttpStatusCode.InternalServerError)]
    [SwaggerResponse((int)HttpStatusCode.OK, "The picture uploaded")]
    [SwaggerOperation(
        Summary = "upload a picture for new picture",
        Description = "This action upload a picture for new picture",
        OperationId = "UploadPictureAsync",
        Tags = new[] { "Pictures:Post" }
    )]
    public async Task<ActionResult> UploadPictureAsync(int storeId, byte 
    sequence, IFormFile file )
    {
       ...
    }
0

All Articles