Swagger UI中multipart/form-data对象数组上传问题及报错求助
问题解决:Swagger UI无法正确绑定对象列表(FromForm/Query参数)
问题概述
- Swagger UI提交包含
List<InterestDto>的请求时,API接收的列表始终为空;Postman构造的请求可正常解析。 - 问题覆盖
multipart/form-data([FromForm]绑定)和Query参数两种场景。 - 尝试自定义
CustomOperationFilter时触发KeyNotFoundException,提示找不到"Interests"键。
错误信息
System.Collections.Generic.KeyNotFoundException HResult=0x80131577 Message=The given key 'Interests' was not present in the dictionary. Source=System.Private.CoreLib StackTrace: at System.ThrowHelper.ThrowKeyNotFoundException[T](T key) at System.Collections.Generic.Dictionary`2.get_Item(TKey key) at Flats4us.Helpers.CustomOperationFilter.Apply(OpenApiOperation operation, OperationFilterContext context) in C:\Dane\Projekty\Flats4Us\Flats4us\Flats4us\Helpers\CustomOperationFilter.cs:line 23 at Swashbuckle.AspNetCore.SwaggerGen.SwaggerGenerator.GenerateOperation(ApiDescription apiDescription, SchemaRepository schemaRepository)
相关代码(原代码)
AuthController.cs
[HttpPost("register/Student")] public async Task<ActionResult<User>> RegisterStudentAsync([FromForm] StudentRegisterDto request) { try { await _userService.RegisterStudentAsync(request); return Ok("Registered successfully"); } catch (Exception ex) { return BadRequest(ex.Message); } }
StudentRegisterDto.cs
[ModelBinder(BinderType = typeof(MetadataValueModelBinder))] public class StudentRegisterDto : OwnerStudentRegisterDto { // 其他属性... public List<InterestDto> Interests { get; set; } }
Program.cs
builder.Services.AddSwaggerGen(options => { options.OperationFilter<CustomOperationFilter>(); // 其他配置... }
MetadataValueModelBinder.cs
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() { PropertyNameCaseInsensitive = true }; var deserialized = JsonSerializer.Deserialize(values.FirstValue, bindingContext.ModelType, options); bindingContext.Result = ModelBindingResult.Success(deserialized); return Task.CompletedTask; } }
CustomOperationFilter.cs
public class CustomOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { if (operation.RequestBody != null && operation.RequestBody.Content.TryGetValue("multipart/form-data", out var openApiMediaType)) { var options = new JsonSerializerOptions { WriteIndented = true }; var array = new OpenApiArray { new OpenApiString(JsonSerializer.Serialize(new InterestDto {InterestId = 0, Name="string"}, options)), }; openApiMediaType.Schema.Properties["Interests"].Example = array; }; } }
解决方案
1. 修复CustomOperationFilter的KeyNotFoundException
Swashbuckle默认会将C#的PascalCase属性名转换为驼峰式(如Interests→interests),所以原代码中直接访问"Interests"键会找不到。修改如下:
public class CustomOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { // 处理multipart/form-data场景 if (operation.RequestBody != null && operation.RequestBody.Content.TryGetValue("multipart/form-data", out var openApiMediaType)) { if (openApiMediaType.Schema.Properties.TryGetValue("interests", out var interestsProperty)) { var options = new JsonSerializerOptions { WriteIndented = true }; // 生成JSON数组示例,提示用户输入格式 var exampleJson = JsonSerializer.Serialize(new List<InterestDto> { new InterestDto { InterestId = 0, Name = "阅读" }, new InterestDto { InterestId = 1, Name = "运动" } }, options); // 将Schema类型改为string,让Swagger显示文本框而非拆分字段 interestsProperty.Type = "string"; interestsProperty.Format = null; interestsProperty.Example = new OpenApiString(exampleJson); } } // 处理Query参数场景 foreach (var parameter in operation.Parameters) { if (parameter.Name.Equals("interests", StringComparison.OrdinalIgnoreCase)) { var options = new JsonSerializerOptions { WriteIndented = true }; var exampleJson = JsonSerializer.Serialize(new List<InterestDto> { new InterestDto { InterestId = 0, Name = "阅读" } }, options); parameter.Example = new OpenApiString(exampleJson); parameter.Schema.Type = "string"; } } } }
2. 确保ModelBinder兼容Swagger发送的格式
你的MetadataValueModelBinder已经支持将JSON字符串反序列化为List<InterestDto>,修改后的OperationFilter会让Swagger UI将Interests显示为文本框,用户输入JSON数组即可完成正确绑定。
3. 验证Query参数场景
如果Query参数仍有问题,在Dto的Interests属性上添加标注:
[FromQuery(Name = "Interests")] public List<InterestDto> Interests { get; set; }
配合上面OperationFilter中对Query参数的处理,即可正常解析。
4. 可选:升级Swashbuckle.AspNetCore版本
确保使用最新版本的Swashbuckle.AspNetCore包,新版本优化了复杂对象列表的表单绑定支持,可能减少自定义代码的需求。
内容的提问来源于stack exchange,提问作者giziuuu
相关产品推荐
相关产品推荐

