Asp.net Core接收Shopify Webhook时请求读取异常及超时问题
解决Asp.net接收Shopify Webhook时的Request.Form异常与Body读取超时问题
错误原因分析
System.InvalidOperationException访问Request.Form时抛出:
Shopify Webhook默认以application/json格式发送请求,而非表单格式(application/x-www-form-urlencoded)。此时尝试读取Request.Form,Asp.net会尝试将JSON格式的请求体解析为表单数据,格式不匹配直接抛出异常。读取Request.Body超时:
- Request.Body是只读流,默认只能读取一次,若之前有中间件或绑定逻辑已读取过流,未重置Position会导致后续读取阻塞超时。
- 同步读取流(如
ReadToEnd())易引发线程阻塞,导致超时。
解决方案
方案1:直接用[FromBody]绑定模型(推荐)
利用Asp.net的自动模型绑定,直接将JSON请求体绑定到自定义模型,无需手动读取流:
// 定义对应Shopify Webhook的模型(示例以订单创建Webhook为例) public class ShopifyOrderWebhook { public long Id { get; set; } public string Email { get; set; } // 其他字段根据Shopify Webhook文档补充 } [HttpPost("shopify/webhook/order-created")] public async Task<IActionResult> OrderCreated([FromBody] ShopifyOrderWebhook webhookData) { // 处理Webhook业务逻辑 // 务必验证HMAC签名(见下方补充) return Ok(); }
方案2:手动异步读取Request.Body
若需直接处理原始请求体内容,使用异步方式读取并确保流位置正确:
[HttpPost("shopify/webhook")] public async Task<IActionResult> HandleWebhook() { // 重置流位置到开头(避免之前的读取操作导致流指针在末尾) Request.Body.Position = 0; // 异步读取请求体 using var streamReader = new StreamReader(Request.Body); var rawBody = await streamReader.ReadToEndAsync(); // 解析JSON(可使用System.Text.Json或Newtonsoft.Json) var webhookData = JsonSerializer.Deserialize<YourWebhookModel>(rawBody); // 处理逻辑 + HMAC验证 return Ok(); }
补充:启用Request.Body可重复读取(可选)
如果需要多次读取请求体(比如先验证签名再解析模型),需在配置中开启流缓冲:
在Program.cs中添加:
builder.Services.Configure<Microsoft.AspNetCore.Http.Features.FormOptions>(options => { options.BufferBody = true; options.BufferBodyLengthLimit = 10 * 1024 * 1024; // 10MB,根据Shopify Webhook大小调整 });
关键:验证Shopify Webhook签名
无论哪种方案,都必须验证请求头中的X-Shopify-Hmac-SHA256签名,确保请求合法:
private bool ValidateShopifyHmac(string rawBody, string hmacHeader, string shopifySecret) { var secretBytes = Encoding.UTF8.GetBytes(shopifySecret); var bodyBytes = Encoding.UTF8.GetBytes(rawBody); var hmac = new HMACSHA256(secretBytes); var computedHmac = Convert.ToBase64String(hmac.ComputeHash(bodyBytes)); return computedHmac.Equals(hmacHeader, StringComparison.OrdinalIgnoreCase); }
内容的提问来源于stack exchange,提问作者Jed
相关产品推荐
相关产品推荐

