.NET中Post端点如何同时接收文件与JSON数据(Ardalis Endpoints)
解决Ardalis Endpoints同时接收文件与JSON对象的415错误问题
问题核心
使用Ardalis Endpoints开发POST端点时,期望直接接收ExecutionResult类型的JSON对象与IFormFile文件,但将Result字段改为ExecutionResult并标记[FromBody]、File标记[FromForm],同时给请求类加[FromBody]时,请求返回415不支持媒体类型错误。当前将ExecutionResult序列化为字符串的方式可正常运行,但希望优化为直接绑定复杂类型。
错误原因
ASP.NET Core无法同时从multipart/form-data请求中混合使用[FromBody]和[FromForm]:
[FromBody]期望请求的Content-Type为application/json,但实际发送的是multipart/form-data(包含文件和JSON字段)- 当请求类标记
[FromBody]时,框架会强制要求请求体为纯JSON,与实际的multipart格式冲突,导致415错误
解决方案
1. 调整请求模型与端点配置
移除所有[FromBody]标记,让整个请求模型通过[FromForm]绑定(因为multipart请求的所有数据都属于表单内容),直接将Result字段定义为ExecutionResult类型:
// 请求模型 public class CreateQueryResultRequest { public ExecutionResult Result { get; set; } public IFormFile? File { get; set; } } // 端点代码(仅修改HandleAsync参数与反序列化逻辑) public override async Task<ActionResult<ExecutionResult>> HandleAsync([FromForm] CreateQueryResultRequest request, CancellationToken cancellationToken = default) { if (!ModelState.IsValid) { foreach (var state in ModelState) { foreach (var error in state.Value.Errors) { _logger.LogError("Validation error: {ErrorMessage}", error.ErrorMessage); } } return BadRequest(ModelState); } try { _logger.LogInformation("Handling CreateExecutionResult request"); // 无需手动反序列化,request.Result已直接绑定为ExecutionResult对象 var resultData = request.Result; // 后续业务逻辑保持不变 ... return Created(getByIdUrl, result.Value); } catch (Exception ex) { _logger.LogError(ex, "An error occurred while handling CreateExecutionResult request"); return StatusCode(500, "An unexpected error occurred"); } }
2. 修改请求发送代码
确保发送Result字段时,指定其Content-Type为application/json,让ASP.NET Core的模型绑定器能自动将表单中的JSON字符串反序列化为ExecutionResult对象:
using var httpClient = _httpClientFactory.CreateClient("AppClient"); try { using var multipartContent = new MultipartFormDataContent(); if (data is not null) { // 文件上传逻辑保持不变 var memoryStream = new MemoryStream(); await using var streamWriter = new StreamWriter(memoryStream, Encoding.UTF8); await using JsonTextWriter jsonWriter = new JsonTextWriter(streamWriter); JsonSerializer serializer = new JsonSerializer(); serializer.Serialize(jsonWriter, data); await jsonWriter.FlushAsync(); memoryStream.Position = 0; var streamContent = new StreamContent(memoryStream); streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); multipartContent.Add(streamContent, "File", "data.json"); } // 修改此处:为Result字段指定application/json的Content-Type var metadataJson = JsonConvert.SerializeObject(result); var resultContent = new StringContent(metadataJson, Encoding.UTF8, "application/json"); multipartContent.Add(resultContent, "Result"); HttpResponseMessage response = await httpClient.PostAsync("/tasks/result", multipartContent); var res = await response.Content.ReadAsStringAsync(); if (!response.IsSuccessStatusCode) { return Result.Error(new Error("error", "Error occured while sending data to QueryHub")); } return Result.Success(); } catch (Exception ex) { return Result.Error(new Error("error", ex.Message)); }
3. 确保SmartEnum反序列化支持
由于ExecutionStatusEnum是Ardalis SmartEnum,需确保项目已配置对应的JSON转换器:
- 如果使用Newtonsoft.Json,在Program.cs/Startup.cs中添加:
builder.Services.AddControllers() .AddNewtonsoftJson(options => { options.SerializerSettings.Converters.Add(new SmartEnumConverter()); }); - 如果使用System.Text.Json,添加:
builder.Services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.Converters.Add(new SmartEnumConverter<ExecutionStatusEnum, int>()); });
关键注意事项
- 所有multipart请求的内容都属于表单数据,不能混合
[FromBody]与[FromForm]标记 - 发送JSON字段时必须指定
application/json的Content-Type,否则模型绑定器无法识别并反序列化复杂类型 - 确保SmartEnum的JSON转换器已正确配置,避免枚举值反序列化失败
内容的提问来源于stack exchange,提问作者Hardik Dhuri
相关产品推荐
相关产品推荐

