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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:07:33