webhook 在 asp.net 中需手动实现,核心是处理 post 请求、解析 json、校验签名并返回 200;须配置 web.config 限值、禁用 viewstate、避免 session、安全验签、记录原始请求以支持重试排查。

WebHook 不是 ASP.NET 内置功能,.NET 没有开箱即用的 WebHookController 或自动签名验证中间件(除非你用过已停更的 Microsoft.AspNet.WebHooks.Receivers)。直接在 ASP.NET Web Forms 或 MVC 里“接收回调”,本质就是写一个能处理 POST 请求、解析 JSON、校验签名、返回 200 的普通 HTTP 端点——别被“WebHook”这个词带偏了。
怎么写一个可上线的 WebHook 接收页(.aspx)
PayPal、阿里云、RTC 等平台回调都是标准 POST + JSON Body,PaypalNotifyPage.aspx 这类页面不需要任何特殊基类。关键在 Page_Load 里手动读取原始请求流:
- 必须用
Request.InputStream读取原始 body,Request.Form或Request.Params对 JSON 无效 - 设置
Response.ContentType = "application/json",但返回内容不重要,只要状态码是 200 - 禁用 ViewState 和事件生命周期:在 .aspx 顶部加
EnableViewState="false" AutoEventWireup="false" - 别依赖 Session,回调请求无 Cookie,Session 可能为空或报错
示例片段(PaypalNotifyPage.aspx.cs):
protected void Page_Load(object sender, EventArgs e)
{
if (Request.HttpMethod != "POST")
{
Response.StatusCode = 405;
Response.End();
return;
}
string json;
using (var reader = new StreamReader(Request.InputStream))
{
json = reader.ReadToEnd();
}
// 解析、校验、入库……
var payload = JsonConvert.DeserializeObject<jobject>(json);
bool isValid = VerifyPayPalSignature(payload, Request.Headers["Paypal-Request-Id"]);
if (!isValid)
{
Response.StatusCode = 401;
Response.End();
return;
}
// 处理业务逻辑(异步推荐用 Task.Run,避免阻塞 IIS 线程)
ProcessPayPalEvent(payload);
// 必须返回 200,且不能抛异常
Response.StatusCode = 200;
Response.Write("{\"status\":\"ok\"}");
Response.End();
}</jobject>
为什么 PayPal / 阿里云回调总失败(400/500/超时)
不是代码逻辑错,大概率卡在基础链路:
-
web.config里<httpruntime maxrequestlength="102400" executiontimeout="300"></httpruntime>—— 默认只允许 4MB 请求、110秒超时,PayPal Webhook 可能带大附件字段,阿里云报警回调虽小但重试密集 - IIS 启用了“请求筛选”,默认拒绝非标准 Content-Type;需在
web.config加:<security><requestfiltering allowdoubleescaping="true"><fileextensions allowunlisted="true"></fileextensions></requestfiltering></security> - 没处理 HTTPS 重定向:回调 URL 填的是
https://...,但站点只监听 HTTP,IIS 自动 302 跳转——WebHook 客户端(如 PayPal)不跟跳,直接报 302 或超时 - 防火墙/安全组没放行入站 443 端口,或云服务器没配置白名单 IP(比如阿里云监控回调源 IP 是
106.11.225.0/24等网段)
如何安全验证 WebHook 签名(以 PayPal 为例)
PayPal 要求用 PayPal-Request-Signature + PayPal-Request-Id + PayPal-Request-Timestamp 三者联合验签,不是简单比对 header 里的 token:
- 签名验证必须在业务处理前完成,否则可能被重放攻击
- 不要硬编码密钥,从
web.config的<appsettings></appsettings>或环境变量读取 - 时间戳偏差超过 5 分钟视为无效,需校验
PayPal-Request-Timestamp与服务器当前时间差 - PayPal 的签名算法是 HMAC-SHA256,密钥是你的 Webhook ID 对应的
webhook_secret,不是 Client ID 或 Secret - 验签原文是:HTTP 方法 + 换行 + 请求 URL 路径(不含 query string)+ 换行 + 时间戳 + 换行 + 请求 ID + 换行 + JSON body(原始字节,不可格式化)
漏掉任意一个换行或大小写,验签必败。建议直接用 PayPal 提供的 PayPalHttp SDK 中的 VerifyWebhookEvent 方法,别手写。
回调处理失败后怎么重试和排查
PayPal 重试策略是“最多 3 次,间隔指数退避”,阿里云/RTC 是“3 次,固定 10 秒间隔”。但你的服务没记录原始请求,就等于丢掉了所有线索:
- 务必在验签通过后、业务处理前,把原始
Request.InputStream字节流存到日志文件或数据库(带时间戳、IP、User-Agent) - 不要用
Console.WriteLine或Debug.WriteLine,IIS 下它们不输出;改用System.Diagnostics.EventLog或 NLog 写入文件 - 如果业务处理抛异常,确保
try/catch捕获并记日志,然后仍返回 200 —— 否则平台会不断重试,形成雪崩 - PayPal 控制台能查每条 Webhook 的投递状态和响应 body;阿里云云监控控制台有“测试回调”按钮,可模拟触发并看返回详情
最常被忽略的一点:WebHook 是单向通知,不是 RPC。你不该在回调里调 PayPal API 查订单状态,而应该把事件落库后,由后台定时任务或消息队列驱动后续动作——否则一次回调耗时过长,直接触发平台超时重试。










