当输入参数之一是文件时,Swashbuckle UI 问题

Swashbuckle UI problem when one of the input parameter is a file

我正在尝试上传包含一些元数据的文件:

Upload(IFormFile file, [FromForm]IEnumerable<MetadataValue> list)

public class MetadataValue
{
    public Metadata Metadata { get; set; }
    public string Value { get; set; }
}

public enum Metadata
{
    Cat1,
    Cat2
}

MetadataValue 列表显示为字符串输入数组。我无法确定我应该如何将数据传递给控制器​​ - 我总是收到一个空集合。

当我从 Postman 发送请求时一切正常。这是由点 pattern/notation.

处理的

在 Postman 中,复杂对象的每个参数和每个 属性 都作为单独的字段发送:

curl --location --request POST 'https://localhost:44395/api/document/upload' \
--form 'file=@/C:/FilterDraft.txt' \
--form 'list[0].metadata=Cat1' \
--form 'list[0].value=ABC' \
--form 'list[1].metadata=Cat2' \
--form 'list[1].value=DEF'

如何在 Swagger 中实现此目的 UI?
如何配置 Swagger 以获得更好的 UI 生成?

我正在使用:


解决方案:

要添加示例,您必须使用 SwaggerOperationFilter 修饰操作方法:

[SwaggerOperationFilter(typeof(OperationFilter))]
Upload(IFormFile file, [FromForm]IEnumerable<MetadataValue> list)
[...]
internal class UploadOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation.OperationId != nameof(DocumentController.Upload))
        {
            return;
        }
        if (operation.RequestBody.Content.TryGetValue("multipart/form-data", out var openApiMediaType))
        {
            var options = new JsonSerializerOptions { WriteIndented = true };
            options.Converters.Add(new JsonStringEnumConverter());

            var array = new OpenApiArray
            {
                new OpenApiString(JsonSerializer.Serialize(new MetadataValue {Metadata = Metadata.Cat1, Value = "ABC"}, options)),
                new OpenApiString(JsonSerializer.Serialize(new MetadataValue {Metadata = Metadata.Cat2, Value = "DEF"}, options))
            };

            openApiMediaType.Schema.Properties["metadata"].Example = array;
        }
    }
}

要在控制器中获取值(不是空集合),您必须添加自定义 ModelBinder:

[ModelBinder(BinderType = typeof(MetadataValueModelBinder))]
public class MetadataValue
{
    public Metadata Metadata { get; set; }
    public string Value { get; set; }
}

public class MetadataValueModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            if (bindingContext == null)
                throw new ArgumentNullException(nameof(bindingContext));

            var values = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (values.Length == 0)
                return Task.CompletedTask;

            var options = new JsonSerializerOptions();
            options.Converters.Add(new JsonStringEnumConverter());

            var deserialized = JsonSerializer.Deserialize(values.FirstValue, bindingContext.ModelType, options);

            bindingContext.Result = ModelBindingResult.Success(deserialized);
            return Task.CompletedTask;
        }
    }