Asp.Net Core swagger Help Pages for IFormFile

10,443

Solution 1

For you ASP.NET Core developers, there's an issue written up in the Swashbuckle.AspNetCore GitHub repo for this: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/193. It has some working code for an OperationFilter in the comments as well -- that one worked better for me than the other ones in this question.

Solution 2

I have the same issue and your solution helped me.

I just changed the OperationFilter because my IFormFile parameter was not a nested parameter but the main parameter of the action method.

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" };
    }
}

I suppose you use SwashBuckle to generate the swagger. It would be nice if swashBuckle would handle this. Maybe i'll contribute if i have time left over.

Share:
10,443
YuriyP
Author by

YuriyP

Asp.Net MVC developer at Infoservice, Khmelnitsky, Ukraine

Updated on July 26, 2022

Comments

  • YuriyP
    YuriyP almost 2 years

    I am trying to setup swagger for testing models which have IFormFile properties. For example I have next 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 generate help page which does not allow to test it Wrong swagger ui To solve this problem I wrote next 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 this everething works as I expect from swagger. Correct swagger ui

    For now this solution works for me, but it feels not right. Maybe I am missing some simple solution for this. Also this approach does not handle List or complex object properties with IFormFile or maybe something else.

  • A X
    A X over 3 years
    This doesn't compile