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); (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) private static void ConfigureWeChatFallback(IServiceCollection services)
{ {
services.PostConfigure<WeChatMpOptions>(options => services.PostConfigure<WeChatMpOptions>(options =>

View File

@ -27,6 +27,7 @@ public partial class AuthService
/// </summary> /// </summary>
[AllowAnonymous] [AllowAnonymous]
[HttpGet("/apiwechat/verification")] [HttpGet("/apiwechat/verification")]
[HttpHead("/apiwechat/verification")]
[Produces("text/plain")] [Produces("text/plain")]
[ApiExplorerSettings(GroupName = "WeChat")] [ApiExplorerSettings(GroupName = "WeChat")]
public async Task<ContentResult> WeChatValidateLegacyAsync( public async Task<ContentResult> WeChatValidateLegacyAsync(
@ -35,10 +36,36 @@ public partial class AuthService
[FromQuery] string? nonce, [FromQuery] string? nonce,
[FromQuery] string? echostr, [FromQuery] string? echostr,
CancellationToken cancellationToken = default) CancellationToken cancellationToken = default)
{
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); var options = await WeChatMpConfigProvider.GetAsync(cancellationToken);
return WeChatMpCallbackSupport.Validate(options, signature, timestamp, nonce, echostr); return WeChatMpCallbackSupport.Validate(options, signature, timestamp, nonce, echostr);
} }
catch (Exception ex)
{
WeChatLogger.LogWarning(ex, "微信公众号验签读取配置失败");
return WeChatMpCallbackSupport.PlainError();
}
}
/// <summary> /// <summary>
/// 微信服务器接入验签。 /// 微信服务器接入验签。

View File

@ -17,7 +17,7 @@ internal static class WeChatMpCallbackSupport
// 微信服务器要求 HTTP 200 + 纯文本 echostr,不可抛 BusinessException(会变成 JSON 非 200) // 微信服务器要求 HTTP 200 + 纯文本 echostr,不可抛 BusinessException(会变成 JSON 非 200)
if (string.IsNullOrWhiteSpace(options.Token)) if (string.IsNullOrWhiteSpace(options.Token))
{ {
return PlainText("error"); return PlainError();
} }
if (string.IsNullOrWhiteSpace(signature) if (string.IsNullOrWhiteSpace(signature)
@ -25,13 +25,19 @@ internal static class WeChatMpCallbackSupport
|| string.IsNullOrWhiteSpace(nonce) || string.IsNullOrWhiteSpace(nonce)
|| string.IsNullOrWhiteSpace(echostr)) || string.IsNullOrWhiteSpace(echostr))
{ {
return PlainText("error"); return PlainError();
} }
var valid = WeChatSignatureHelper.Validate(options.Token, timestamp, nonce, signature); var valid = WeChatSignatureHelper.Validate(options.Token, timestamp, nonce, signature);
return PlainText(valid ? echostr : "error"); 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( public static async Task<ContentResult> HandleMessageAsync(
WeChatMpOptions options, WeChatMpOptions options,
IWeChatEventHandler eventHandler, IWeChatEventHandler eventHandler,