fix(wechat): 验签接口支持 HEAD 并避免配置异常返回非 200

微信后台/探活对 HEAD 会收到 405;读取 wechat_mp 配置异常时改为 HTTP 200 + error,与 WMS 行为一致。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zzh 2026-07-02 12:15:25 +08:00
parent 7257bf869e
commit 76ff60e793
3 changed files with 48 additions and 4 deletions

View File

@ -46,6 +46,17 @@ public class WeChatRouteIntegrationTests
(await response.Content.ReadAsStringAsync()).Should().Be(echostr);
}
[Fact]
public async Task WeChatValidateLegacy_Head_ShouldReturn200()
{
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: ConfigureWeChatFallback);
var request = new HttpRequestMessage(HttpMethod.Head, "/apiwechat/verification");
var response = await host.Client.SendAsync(request);
response.StatusCode.Should().Be(HttpStatusCode.OK);
}
private static void ConfigureWeChatFallback(IServiceCollection services)
{
services.PostConfigure<WeChatMpOptions>(options =>

View File

@ -27,6 +27,7 @@ public partial class AuthService
/// </summary>
[AllowAnonymous]
[HttpGet("/apiwechat/verification")]
[HttpHead("/apiwechat/verification")]
[Produces("text/plain")]
[ApiExplorerSettings(GroupName = "WeChat")]
public async Task<ContentResult> WeChatValidateLegacyAsync(
@ -36,8 +37,34 @@ public partial class AuthService
[FromQuery] string? echostr,
CancellationToken cancellationToken = default)
{
var options = await WeChatMpConfigProvider.GetAsync(cancellationToken);
return WeChatMpCallbackSupport.Validate(options, signature, timestamp, nonce, echostr);
if (string.Equals(
httpContextAccessor.HttpContext?.Request.Method,
HttpMethods.Head,
StringComparison.OrdinalIgnoreCase))
{
return WeChatMpCallbackSupport.HeadOk();
}
return await WeChatValidateCoreAsync(signature, timestamp, nonce, echostr, cancellationToken);
}
private async Task<ContentResult> WeChatValidateCoreAsync(
string? signature,
string? timestamp,
string? nonce,
string? echostr,
CancellationToken cancellationToken)
{
try
{
var options = await WeChatMpConfigProvider.GetAsync(cancellationToken);
return WeChatMpCallbackSupport.Validate(options, signature, timestamp, nonce, echostr);
}
catch (Exception ex)
{
WeChatLogger.LogWarning(ex, "微信公众号验签读取配置失败");
return WeChatMpCallbackSupport.PlainError();
}
}
/// <summary>

View File

@ -17,7 +17,7 @@ internal static class WeChatMpCallbackSupport
// 微信服务器要求 HTTP 200 + 纯文本 echostr,不可抛 BusinessException(会变成 JSON 非 200)
if (string.IsNullOrWhiteSpace(options.Token))
{
return PlainText("error");
return PlainError();
}
if (string.IsNullOrWhiteSpace(signature)
@ -25,13 +25,19 @@ internal static class WeChatMpCallbackSupport
|| string.IsNullOrWhiteSpace(nonce)
|| string.IsNullOrWhiteSpace(echostr))
{
return PlainText("error");
return PlainError();
}
var valid = WeChatSignatureHelper.Validate(options.Token, timestamp, nonce, signature);
return PlainText(valid ? echostr : "error");
}
/// <summary>微信或探活可能发 HEAD,须返回 200(WMS/Spring 默认支持)。</summary>
public static ContentResult HeadOk() =>
new() { Content = string.Empty, ContentType = "text/plain; charset=utf-8", StatusCode = StatusCodes.Status200OK };
public static ContentResult PlainError() => PlainText("error");
public static async Task<ContentResult> HandleMessageAsync(
WeChatMpOptions options,
IWeChatEventHandler eventHandler,