PAYATHON 2026

C# 如何接收 Incoming POST Data?

支付老李

结论

application/x-www-form-urlencoded 数据无需手动读取请求体。如果回调方法是 ASP.NET Controller 的 Action,可以直接使用表单模型绑定,把 POST 字段映射到 CgCallback 对象。

Request 报错通常是因为该方法不在具有 HTTP 请求上下文的 Controller 中,或者当前类没有继承相应的 ASP.NET 基类。相比逐个读取 Request.Form,更适合使用 [FromForm] 接收数据。

ASP.NET Core 的写法

先定义回调模型。表单字段名是 order_id,而 C# 属性名是 OrderId,因此需要明确指定两者的映射关系:

using Microsoft.AspNetCore.Mvc;

public class CgCallback
{
    [FromForm(Name = "id")]
    public string? Id { get; set; }

    [FromForm(Name = "order_id")]
    public string? OrderId { get; set; }
}

然后在 Controller 中接收 POST 回调:

using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("[controller]")]
public class PaymentController : ControllerBase
{
    [HttpPost("/CgCallback")]
    [Consumes("application/x-www-form-urlencoded")]
    public IActionResult ReceiveCallback([FromForm] CgCallback callback)
    {
        if (string.IsNullOrWhiteSpace(callback.Id) ||
            string.IsNullOrWhiteSpace(callback.OrderId))
        {
            return BadRequest("Missing required callback fields.");
        }

        // 在这里保存或处理 callback
        // 例如:
        // await _paymentService.ProcessCallbackAsync(callback);

        return Ok(callback);
    }
}

回调地址是:

POST https://example.com/CgCallback

假设请求体为:

id=123&order_id=ORDER-456

框架会映射为:

callback.Id       // "123"
callback.OrderId  // "ORDER-456"

如果属性上的 [FromForm] 不生效,也可以使用通用的模型绑定名称:

using Microsoft.AspNetCore.Mvc.ModelBinding;

public class CgCallback
{
    [BindNever]
    public string? InternalValue { get; set; }

    [BindProperty(Name = "id")]
    public string? Id { get; set; }

    [BindProperty(Name = "order_id")]
    public string? OrderId { get; set; }
}

不过在 ASP.NET Core Web API 中,通常直接在 Action 参数上使用 [FromForm],再为名称不一致的字段指定映射名称。这样写更直观。

手动读取表单字段

如果回调字段不固定,或者需要访问原始表单集合,可以在 Controller Action 中读取 Request.Form:

[HttpPost("/CgCallback")]
public async Task<IActionResult> ReceiveCallback()
{
    if (!Request.HasFormContentType)
    {
        return BadRequest("Expected form-urlencoded data.");
    }

    IFormCollection form = await Request.ReadFormAsync();

    var callback = new CgCallback
    {
        Id = form["id"].ToString(),
        OrderId = form["order_id"].ToString()
    };

    return Ok(callback);
}

这里可以使用 Request,因为该方法位于继承自 ControllerBase 或 Controller 的 Controller 中。

不要把接收回调的方法写成普通类方法:

public CgCallback IncomingData()
{
    // 普通类中没有 Controller 提供的 Request 属性
}

如果业务处理逻辑位于普通服务类中,应先由 Controller 接收请求,再把模型传给服务:

[HttpPost("/CgCallback")]
public IActionResult ReceiveCallback([FromForm] CgCallback callback)
{
    _paymentService.Process(callback);
    return Ok();
}

ASP.NET Framework MVC 的写法

如果项目使用旧版 ASP.NET MVC,而不是 ASP.NET Core,可以直接通过 Action 参数进行模型绑定:

public class CgCallback
{
    public string Id { get; set; }

    public string OrderId { get; set; }
}
using System.Web.Mvc;

public class PaymentController : Controller
{
    [HttpPost]
    public ActionResult CgCallback()
    {
        var callback = new CgCallback
        {
            Id = Request.Form["id"],
            OrderId = Request.Form["order_id"]
        };

        return Json(callback);
    }
}

此时,Request 来自 System.Web.Mvc.Controller。如果当前类没有继承 Controller,同样会出现以下错误:

The name 'Request' does not exist in the current context

路由需要与回调地址一致

只把方法命名为 CgCallback,不一定会自动生成 /CgCallback 路由。在 ASP.NET Core 中,可以明确声明绝对路由:

[HttpPost("/CgCallback")]
public IActionResult ReceiveCallback([FromForm] CgCallback callback)
{
    return Ok();
}

也可以使用 Controller 路由:

[Route("payment")]
public class PaymentController : ControllerBase
{
    [HttpPost("callback")]
    public IActionResult ReceiveCallback([FromForm] CgCallback callback)
    {
        return Ok();
    }
}

对应地址是:

https://example.com/payment/callback

注意事项

支付回调不能只根据 id 和 order_id 更新订单状态,还需要注意以下事项:

  • 按照 API 文档验证签名、时间戳或回调令牌。
  • 核对订单号、金额、币种和支付状态是否与本地订单一致。
  • 做好幂等处理,因为支付平台可能重复发送同一回调。
  • 不要只根据客户端跳转页面判断支付是否成功。
  • 按照 API 要求返回指定的状态码或确认文本。某些平台要求 200 OK,另一些平台要求返回固定字符串,具体以对方的接口文档为准。
  • 不要在日志中记录银行卡信息、密钥、完整签名或其他敏感数据。

对于当前场景,ASP.NET Core 中可以采用下面的简洁写法:

[HttpPost("/CgCallback")]
public IActionResult ReceiveCallback([FromForm] CgCallback callback)
{
    // 验证并处理 callback
    return Ok();
}

该方法必须是 Controller Action,并通过 [FromForm] 告诉 ASP.NET Core 从 application/x-www-form-urlencoded 表单中绑定模型。

备注:内容仅供参考。