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

.NET 5 Web API接收Webhook POST请求时string入参为null

.NET 5 Web API 接收JSON类型Webhook原始字符串方案

问题说明

使用.NET 5开发Web API接口接收Webhook推送时,存在以下现象:

  • 推送请求头为Content-Type: application/json; charset=UTF-8,请求体为标准JSON结构,示例如下:
{
    "user_ip": "00.00.00.00",
    "date_start": "2022-01-01T14:05:27.546Z",
    "user_referrer": "N/A",
    "user_os": "Windows (deprecated)",
    "id": "000001",
    "items": [
        {
            "id": "1",
            "position": 2,
            "value": "01/01/2022"
        },
        {
            "id": "0",
            "position": 8,
            "value": "Smith"
        },
        {
            "id": "1",
            "position": 22,
            "value": "7843377133"
        },
        {
            "values": [
                {
                    "position": 2,
                    "value": "Coffee"
                },
                {
                    "position": 5,
                    "value": "Tea"
                }
            ],
            "id": "14",
            "position": 7
        },
        {
            "id": "15",
            "position": 8,
            "value": "This is a test for API"
        }
    ],
    "user_device": "Desktop"
}
  • 若接口定义与JSON结构匹配的实体类作为[FromBody]入参,框架可正常完成反序列化;若直接用string类型作为[FromBody]入参,参数值始终为null,错误写法示例:
[ApiController]
[Route("api/webhooks/[controller]")]
public class FormController : BaseApiController
{
    [HttpPost("Form")]
    public async Task<ActionResult> Form([FromBody] string val)
    {
        FormDataModel FS = new FormDataModel(){
            ResultJsonString = val  
        };
        return Ok(new {Data = FS});
    }
}
  • 业务场景不需要框架自动反序列化对象,需要直接获取原始JSON请求字符串。

问题原因

.NET 5默认内置的System.Text.Json输入格式化器,处理application/json类型请求时,默认将请求体作为完整JSON值反序列化为目标参数类型:当参数类型为string时,格式化器会要求请求体是一个被双引号包裹的JSON字符串字面量(例如"\"test content\""),直接传入JSON对象/数组结构时,反序列化string类型失败,就会给参数赋值null。

可行解决方案

以下方案均可实现原始JSON字符串获取,可根据业务场景选择:

  • 方案1:直接读取请求体流(通用性最高)
    不依赖框架模型绑定,直接从请求对象的Body流中读取原始内容,不需要修改全局配置,适合单个接口需要获取原始请求的场景。注意ASP.NET Core默认请求体流只能读取一次,读取前需要开启缓冲,读取完成后重置流位置,避免后续组件读取失败。
    代码实现:
[ApiController]
[Route("api/webhooks/[controller]")]
public class FormController : BaseApiController
{
    [HttpPost("Form")]
    public async Task<ActionResult> Form()
    {
        // 开启请求缓冲,支持多次读取请求体
        Request.EnableBuffering();
        
        // 按UTF8编码读取原始请求内容,与推送方编码保持一致
        using var reader = new StreamReader(
            Request.Body, 
            Encoding.UTF8, 
            leaveOpen: true);
        string rawJson = await reader.ReadToEndAsync();
        
        // 重置请求体流位置到起点,不影响后续中间件/过滤器读取
        Request.Body.Position = 0;

        FormDataModel FS = new FormDataModel()
        {
            ResultJsonString = rawJson  
        };

        return Ok(new {Data = FS});
    }
}
  • 方案2:自定义InputFormatter(保留原有接口签名)
    自定义输入格式化器,指定当[FromBody]参数类型为string、请求类型为application/json时,直接读取请求体原始字符串返回,注册到全局MVC配置后,原有[FromBody] string val的写法即可正常工作。
    第一步,自定义格式化类:
public class RawJsonStringInputFormatter : TextInputFormatter
{
    public RawJsonStringInputFormatter()
    {
        // 配置支持的媒体类型和编码
        SupportedMediaTypes.Add("application/json");
        SupportedEncodings.Add(Encoding.UTF8);
    }

    protected override bool CanReadType(Type type)
    {
        // 仅对string类型参数生效
        return type == typeof(string);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(
        InputFormatterContext context, 
        Encoding encoding)
    {
        using var reader = new StreamReader(
            context.HttpContext.Request.Body, 
            encoding, 
            leaveOpen: true);
        string rawContent = await reader.ReadToEndAsync();
        return await InputFormatterResult.SuccessAsync(rawContent);
    }
}

第二步,在Startup.ConfigureServices中注册格式化器,注意插入到格式化器列表的最前端,优先于默认JSON格式化器生效:

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.InputFormatters.Insert(0, new RawJsonStringInputFormatter());
    });
}
  • 方案3:使用JsonElement接收(零配置最简实现)
    System.Text.Json原生支持将JSON请求绑定到JsonElement类型,绑定完成后调用GetRawText()方法即可直接获取原始JSON字符串,不需要修改全局配置,也不需要手动处理流。
    代码实现:
[HttpPost("Form")]
public async Task<ActionResult> Form([FromBody] JsonElement val)
{
    // 获取原始JSON文本
    string rawJson = val.GetRawText();

    FormDataModel FS = new FormDataModel()
    {
        ResultJsonString = rawJson  
    };

    return Ok(new {Data = FS});
}

注意:该方案下框架会先对请求JSON做格式校验,如果JSON格式不合法会直接返回400错误,无法拿到原始错误内容,若业务需要记录格式非法的请求原文,不要使用该方案。


内容的提问来源于stack exchange,提问作者Jet813

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:06:34