Swagger 将 IFormFile 参数列为 "object" 类型

Swagger listing IFormFile parameter as type "object"

我有一个控制器请求一个包含 IFormFile 作为其属性之一的模型。对于请求描述,Swagger UI(我正在为 .NET Core 使用 Swashbuckle 和 OpenApi 3.0)将文件的类型 属性 列为类型对象。有什么方法可以使 Swagger UI 表示准确的类型,并且它是 JSON 表示来帮助客户?

请求模型的控制器如下所示。

[HttpPost]
[Consumes("multipart/form-data")
public async Task<IActionResult> CreateSomethingAndUploadFile ([FromForm]RequestModel model)
{
    // do something
}

模型定义如下:

public class AssetCreationModel
{
    [Required}
    public string Filename { get; set; }

    [Required]
    public IFormFile File { get; set; }       
}

此问题已在以下 github issue/thread 中解决。

此改进已合并到 Swashbuckle.AspNetCore master(根据 2018 年 10 月 30 日),但我不希望它很快作为一个包提供。

如果您只有一个 IFormFile 作为参数,则有一些简单的解决方案。

public async Task UploadFile(IFormFile filePayload){}

简单的可以看下面answer.

集装箱箱这种比较复杂的情况,可以看看下面的answer

internal class FormFileOperationFilter : IOperationFilter
{
    private struct ContainerParameterData
    {
        public readonly ParameterDescriptor Parameter;
        public readonly PropertyInfo Property;

        public string FullName => $"{Parameter.Name}.{Property.Name}";
        public string Name => Property.Name;

        public ContainerParameterData(ParameterDescriptor parameter, PropertyInfo property)
        {
            Parameter = parameter;
            Property = property;
        }
    }

    private static readonly ImmutableArray<string> iFormFilePropertyNames =
        typeof(IFormFile).GetTypeInfo().DeclaredProperties.Select(p => p.Name).ToImmutableArray();

    public void Apply(Operation operation, OperationFilterContext context)
    {
        var parameters = operation.Parameters;
        if (parameters == null)
            return;

        var @params = context.ApiDescription.ActionDescriptor.Parameters;
        if (parameters.Count == @params.Count)
            return;

        var formFileParams =
            (from parameter in @params
                where parameter.ParameterType.IsAssignableFrom(typeof(IFormFile))
                select parameter).ToArray();

        var iFormFileType = typeof(IFormFile).GetTypeInfo();
        var containerParams =
            @params.Select(p => new KeyValuePair<ParameterDescriptor, PropertyInfo[]>(
                p, p.ParameterType.GetProperties()))
            .Where(pp => pp.Value.Any(p => iFormFileType.IsAssignableFrom(p.PropertyType)))
            .SelectMany(p => p.Value.Select(pp => new ContainerParameterData(p.Key, pp)))
            .ToImmutableArray();

        if (!(formFileParams.Any() || containerParams.Any()))
            return;

        var consumes = operation.Consumes;
        consumes.Clear();
        consumes.Add("application/form-data");

        if (!containerParams.Any())
        {
            var nonIFormFileProperties =
                parameters.Where(p =>
                    !(iFormFilePropertyNames.Contains(p.Name)
                    && string.Compare(p.In, "formData", StringComparison.OrdinalIgnoreCase) == 0))
                    .ToImmutableArray();

            parameters.Clear();
            foreach (var parameter in nonIFormFileProperties) parameters.Add(parameter);

            foreach (var parameter in formFileParams)
            {
                parameters.Add(new NonBodyParameter
                {
                    Name = parameter.Name,
                    //Required = , // TODO: find a way to determine
                    Type = "file"
                });
            }
        }
        else
        {
            var paramsToRemove = new List<IParameter>();
            foreach (var parameter in containerParams)
            {
                var parameterFilter = parameter.Property.Name + ".";
                paramsToRemove.AddRange(from p in parameters
                                        where p.Name.StartsWith(parameterFilter)
                                        select p);
            }
            paramsToRemove.ForEach(x => parameters.Remove(x));

            foreach (var parameter in containerParams)
            {
                if (iFormFileType.IsAssignableFrom(parameter.Property.PropertyType))
                {
                    var originalParameter = parameters.FirstOrDefault(param => param.Name == parameter.Name);
                    parameters.Remove(originalParameter);

                    parameters.Add(new NonBodyParameter
                    {
                        Name = parameter.Name,
                        Required = originalParameter.Required,
                        Type = "file",
                        In = "formData"
                    });
                }
            }
        }
    }
}

您需要研究如何添加适合您情况的 some/an OperationFilter

我们今天一直在探索这个问题。如果将以下内容添加到启动中,它将 IFormFile 转换为正确的类型

services.AddSwaggerGen(c => {
   c.SchemaRegistryOptions.CustomTypeMappings.Add(typeof(IFormFile), () => new Schema() { Type = "file", Format = "binary"});
});

另请参阅以下有关 .net core 中文件上传的文章 https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-2.1