You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core Web API中如何通过[FromForm]接收包含IFormFile的对象数组并兼容Swagger UI?

ASP.NET Core Web API中如何通过[FromForm]接收包含IFormFile的对象数组并兼容Swagger UI?

我之前也碰到过一模一样的问题!默认的模型绑定和Swashbuckle对这种带文件的复杂对象数组支持确实不太友好,不过咱们可以分两步搞定:先解决模型绑定的正确性,再让Swagger UI也能正常使用。

第一步:让模型绑定正确接收数组

首先,ASP.NET Core的form-data模型绑定对数组的命名有明确要求,你得用索引式字段名来提交每个FormItem的属性。举个例子:

  • 第一个键值对类型的FormItem:
    • 字段名:formFields[0].key,值填你的键名(比如username)
    • 字段名:formFields[0].value,值填对应的字符串(比如john_doe)
  • 第二个文件类型的FormItem:
    • 字段名:formFields[1].key,值填avatar
    • 字段名:formFields[1].file,选择要上传的文件

另外,别忘了给FormItem加验证,确保每个项要么有value要么有file,不能同时存在或者都没有。写个自定义验证特性就行:

public class FormItemValidationAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var item = value as FormItem;
        if (item == null) return ValidationResult.Success;

        bool hasValue = !string.IsNullOrEmpty(item.value);
        bool hasFile = item.file != null;

        if (hasValue == hasFile)
        {
            return new ValidationResult("每个FormItem必须且只能提供value或file中的一个");
        }

        return ValidationResult.Success;
    }
}

然后把这个特性加到FormItem类上:

[FormItemValidation]
public class FormItem 
{ 
    public string key { get; set; } 
    public string value { get; set; } 
    public IFormFile file { get; set; } 
}

最后在控制器action里加上模型状态检查,确保输入合法:

[HttpPost("{businessKycId}")]
public async Task<ActionResult> SaveAdditionalInfo(
    [FromForm] FormItem[] formFields, 
    [FromRoute] Guid businessKycId, 
    [FromQuery] string token) 
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    // 这里写你的业务逻辑
    return Ok();
}

第二步:让Swagger UI支持数组和文件上传

默认的Swashbuckle不会自动渲染这种带文件的数组,咱们得自定义两个过滤器来调整Swagger的生成逻辑:

1. 编写Schema过滤器,定义数组项的结构

这个过滤器用来告诉SwaggerFormItem[]的正确结构:

public class FormItemArraySchemaFilter : ISchemaFilter
{
    public void Apply(OpenApiSchema schema, SchemaFilterContext context)
    {
        if (context.Type == typeof(FormItem[]))
        {
            schema.Type = "array";
            schema.Items = new OpenApiSchema
            {
                Type = "object",
                Properties = new Dictionary<string, OpenApiSchema>
                {
                    {"key", new OpenApiSchema { Type = "string", Required = new HashSet<string> { "key" }}},
                    {"value", new OpenApiSchema { Type = "string", Nullable = true }},
                    {"file", new OpenApiSchema { Type = "string", Format = "binary", Nullable = true }}
                }
            };
        }
    }
}

2. 编写Operation过滤器,调整请求体的格式

这个过滤器用来把formFields参数转换成multipart/form-data的数组格式,同时保留路由和查询参数:

public class FormItemArrayOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var formParam = operation.Parameters.FirstOrDefault(p => p.Name == "formFields");
        if (formParam != null)
        {
            // 移除原来的参数定义
            operation.Parameters.Remove(formParam);
            
            // 配置multipart/form-data请求体
            if (operation.RequestBody == null)
            {
                operation.RequestBody = new OpenApiRequestBody();
            }

            var mediaType = new OpenApiMediaType
            {
                Schema = new OpenApiSchema
                {
                    Type = "object",
                    Properties = new Dictionary<string, OpenApiSchema>
                    {
                        ["formFields"] = new OpenApiSchema
                        {
                            Type = "array",
                            Items = new OpenApiSchema
                            {
                                Type = "object",
                                Properties = new Dictionary<string, OpenApiSchema>
                                {
                                    {"key", new OpenApiSchema { Type = "string", Required = new HashSet<string> { "key" }}},
                                    {"value", new OpenApiSchema { Type = "string", Nullable = true }},
                                    {"file", new OpenApiSchema { Type = "string", Format = "binary", Nullable = true }}
                                }
                            }
                        }
                    },
                    Required = new HashSet<string> { "formFields" }
                }
            };

            // 设置编码规则,让Swagger正确处理数组
            mediaType.Encoding["formFields"] = new OpenApiEncoding
            {
                Style = ParameterStyle.Form,
                Explode = true
            };

            operation.RequestBody.Content["multipart/form-data"] = mediaType;
        }
    }
}

3. 注册过滤器到Swagger服务

在Program.cs里把这两个过滤器加进去:

builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "你的API名称", Version = "v1" });
    // 添加自定义过滤器
    c.SchemaFilter<FormItemArraySchemaFilter>();
    c.OperationFilter<FormItemArrayOperationFilter>();
});

最后测试一下

现在不管是用Postman按照索引字段名提交,还是在Swagger UI里点击“Add item”添加多个FormItem,选择输入value或者上传file,模型绑定都会正确接收,而且Swagger也能正常渲染表单啦!

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 11:44:35