fix: 微信回调路由挂到 AuthService 以修复 404
Fake 框架未注册 SimpleAdmin.Api.WeChat 下的 ApplicationService, 将 /apiwechat/verification 等公众号验签与消息接口并入 AuthService。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
26ccc69dc6
commit
42b41e68c7
@ -0,0 +1,56 @@
|
||||
using System.Net;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SimpleAdmin.Api.IntegrationTests.Support;
|
||||
using SimpleAdmin.Api.WeChat.Options;
|
||||
|
||||
namespace SimpleAdmin.Api.IntegrationTests;
|
||||
|
||||
public class WeChatRouteIntegrationTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task WeChatRoutes_ShouldBeRegisteredOnAuthService()
|
||||
{
|
||||
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services =>
|
||||
{
|
||||
services.PostConfigure<WeChatMpOptions>(options =>
|
||||
{
|
||||
options.Token = "test-token";
|
||||
options.AppId = "wx-test";
|
||||
});
|
||||
});
|
||||
|
||||
host.RoutePatterns.Should().Contain("apiwechat/verification");
|
||||
host.RoutePatterns.Should().Contain("wechat/mp/{appId}");
|
||||
host.RoutePatterns.Should().Contain("wechat/mp/login-qrcode");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task WeChatValidateLegacy_ShouldReturnEchostrWhenSignatureValid()
|
||||
{
|
||||
await using var host = await SimpleAdminApiTestHost.CreateAsync(configureServices: services =>
|
||||
{
|
||||
services.PostConfigure<WeChatMpOptions>(options => options.Token = "test-token");
|
||||
});
|
||||
|
||||
const string timestamp = "1400000000";
|
||||
const string nonce = "nonce";
|
||||
const string echostr = "hello-wechat";
|
||||
var signature = SignWeChat("test-token", timestamp, nonce);
|
||||
|
||||
var response = await host.Client.GetAsync(
|
||||
$"/apiwechat/verification?signature={signature}×tamp={timestamp}&nonce={nonce}&echostr={echostr}");
|
||||
|
||||
response.StatusCode.Should().Be(HttpStatusCode.OK);
|
||||
(await response.Content.ReadAsStringAsync()).Should().Be(echostr);
|
||||
}
|
||||
|
||||
private static string SignWeChat(string token, string timestamp, string nonce)
|
||||
{
|
||||
var parts = new[] { token, timestamp, nonce }.OrderBy(x => x, StringComparer.Ordinal).ToArray();
|
||||
var hash = SHA1.HashData(Encoding.UTF8.GetBytes(string.Concat(parts)));
|
||||
return Convert.ToHexString(hash).ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
145
src/SimpleAdmin.Api/Auth/AuthService.WeChatMp.cs
Normal file
145
src/SimpleAdmin.Api/Auth/AuthService.WeChatMp.cs
Normal file
@ -0,0 +1,145 @@
|
||||
using Fake.Application;
|
||||
using Fake.Domain.Exceptions;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SimpleAdmin.Api.WeChat;
|
||||
using SimpleAdmin.Api.WeChat.Dtos;
|
||||
using SimpleAdmin.Api.WeChat.Options;
|
||||
using SimpleAdmin.Api.WeChat.Services;
|
||||
|
||||
namespace SimpleAdmin.Api.Auth;
|
||||
|
||||
public partial class AuthService
|
||||
{
|
||||
private IOptions<WeChatMpOptions> WeChatMpOptions =>
|
||||
LazyServiceProvider.GetRequiredService<IOptions<WeChatMpOptions>>();
|
||||
|
||||
private IWeChatMpApiClient WeChatMpApiClient =>
|
||||
LazyServiceProvider.GetRequiredService<IWeChatMpApiClient>();
|
||||
|
||||
private IWeChatEventHandler WeChatEventHandler =>
|
||||
LazyServiceProvider.GetRequiredService<IWeChatEventHandler>();
|
||||
|
||||
private ILogger<AuthService> WeChatLogger =>
|
||||
LazyServiceProvider.GetRequiredService<ILogger<AuthService>>();
|
||||
|
||||
/// <summary>
|
||||
/// 微信服务器接入验签(兼容 WMS:/api/apiwechat/verification)。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpGet("/apiwechat/verification")]
|
||||
[Produces("text/plain")]
|
||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||
public ContentResult WeChatValidateLegacyAsync(
|
||||
[FromQuery] string? signature,
|
||||
[FromQuery] string? timestamp,
|
||||
[FromQuery] string? nonce,
|
||||
[FromQuery] string? echostr)
|
||||
=> WeChatMpCallbackSupport.Validate(WeChatMpOptions, signature, timestamp, nonce, echostr);
|
||||
|
||||
/// <summary>
|
||||
/// 微信服务器接入验签。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpGet("/wechat/mp/{appId}")]
|
||||
[Produces("text/plain")]
|
||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||
public ContentResult WeChatValidateAsync(
|
||||
[FromRoute] string appId,
|
||||
[FromQuery] string? signature,
|
||||
[FromQuery] string? timestamp,
|
||||
[FromQuery] string? nonce,
|
||||
[FromQuery] string? echostr)
|
||||
{
|
||||
WeChatMpCallbackSupport.EnsureAppId(WeChatMpOptions, appId);
|
||||
return WeChatMpCallbackSupport.Validate(WeChatMpOptions, signature, timestamp, nonce, echostr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信消息/事件回调(兼容 WMS:/api/apiwechat/verification)。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/apiwechat/verification")]
|
||||
[Consumes("text/xml", "application/xml", "text/plain")]
|
||||
[Produces("application/xml")]
|
||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||
public Task<ContentResult> WeChatMessageLegacyAsync(
|
||||
[FromQuery] string signature,
|
||||
[FromQuery] string timestamp,
|
||||
[FromQuery] string nonce,
|
||||
[FromQuery(Name = "encrypt_type")] string? encryptType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var request = httpContextAccessor.HttpContext?.Request
|
||||
?? throw new BusinessException("无法读取微信回调请求");
|
||||
return WeChatMpCallbackSupport.HandleMessageAsync(
|
||||
WeChatMpOptions,
|
||||
WeChatEventHandler,
|
||||
request,
|
||||
WeChatLogger,
|
||||
signature,
|
||||
timestamp,
|
||||
nonce,
|
||||
encryptType,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信消息/事件回调。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("/wechat/mp/{appId}")]
|
||||
[Consumes("text/xml", "application/xml", "text/plain")]
|
||||
[Produces("application/xml")]
|
||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||
public async Task<ContentResult> WeChatMessageAsync(
|
||||
[FromRoute] string appId,
|
||||
[FromQuery] string signature,
|
||||
[FromQuery] string timestamp,
|
||||
[FromQuery] string nonce,
|
||||
[FromQuery(Name = "encrypt_type")] string? encryptType,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
WeChatMpCallbackSupport.EnsureAppId(WeChatMpOptions, appId);
|
||||
var request = httpContextAccessor.HttpContext?.Request
|
||||
?? throw new BusinessException("无法读取微信回调请求");
|
||||
return await WeChatMpCallbackSupport.HandleMessageAsync(
|
||||
WeChatMpOptions,
|
||||
WeChatEventHandler,
|
||||
request,
|
||||
WeChatLogger,
|
||||
signature,
|
||||
timestamp,
|
||||
nonce,
|
||||
encryptType,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 PC 扫码登录二维码。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[HttpGet("/wechat/mp/login-qrcode")]
|
||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||
public async Task<WxLoginQrcodeOutput> GetWeChatLoginQrcodeAsync(
|
||||
[FromQuery] string loginUuid,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(loginUuid))
|
||||
{
|
||||
throw new BusinessException("缺少 loginUuid");
|
||||
}
|
||||
|
||||
const int expireSeconds = 600;
|
||||
var qrCodeUrl = await WeChatMpApiClient.CreateLoginQrCodeUrlAsync(
|
||||
loginUuid.Trim(),
|
||||
expireSeconds,
|
||||
cancellationToken);
|
||||
return new WxLoginQrcodeOutput
|
||||
{
|
||||
QrCodeUrl = qrCodeUrl,
|
||||
ExpiresIn = expireSeconds
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,7 @@ namespace SimpleAdmin.Api.Auth;
|
||||
/// </summary>
|
||||
[Authorize]
|
||||
[ApiExplorerSettings(GroupName = "RBAC")]
|
||||
public class AuthService(
|
||||
public partial class AuthService(
|
||||
AccountManager accountManager,
|
||||
UserService userService,
|
||||
MenuService menuService,
|
||||
|
||||
@ -50,10 +50,6 @@ public class SimpleAdminApiModule : FakeModule
|
||||
settings.RootPath = "rbac";
|
||||
settings.TypePredicate = type => type == typeof(AuthService);
|
||||
});
|
||||
options.ApplicationServices2Controller<SimpleAdminApiModule>(settings =>
|
||||
{
|
||||
settings.TypePredicate = type => type.Namespace == "SimpleAdmin.Api.WeChat";
|
||||
});
|
||||
options.ApplicationServices2Controller<FakeRbacApplicationModule>(settings =>
|
||||
{
|
||||
settings.RootPath = "rbac";
|
||||
|
||||
@ -5,6 +5,14 @@ namespace SimpleAdmin.Api.WeChat.Services;
|
||||
|
||||
internal static class WeChatSignatureHelper
|
||||
{
|
||||
public static string Sign(string token, string timestamp, string nonce)
|
||||
{
|
||||
var parts = new[] { token, timestamp, nonce }.OrderBy(x => x, StringComparer.Ordinal).ToArray();
|
||||
var raw = string.Concat(parts);
|
||||
var hash = SHA1.HashData(Encoding.UTF8.GetBytes(raw));
|
||||
return Convert.ToHexString(hash).ToLowerInvariant();
|
||||
}
|
||||
|
||||
public static bool Validate(string token, string timestamp, string nonce, string signature)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(token)
|
||||
|
||||
@ -21,7 +21,7 @@ namespace SimpleAdmin.Api.WeChat;
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||
public class WeChatCustomerAppService(
|
||||
public class WeChatCustomerService(
|
||||
IWeChatMpApiClient weChatMpApiClient,
|
||||
ICustomerRepository customerRepository,
|
||||
UserService userService,
|
||||
@ -1,188 +0,0 @@
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SimpleAdmin.Api.WeChat.Dtos;
|
||||
using SimpleAdmin.Api.WeChat.Options;
|
||||
using SimpleAdmin.Api.WeChat.Services;
|
||||
|
||||
namespace SimpleAdmin.Api.WeChat;
|
||||
|
||||
/// <summary>
|
||||
/// 微信公众号服务器回调与登录二维码。
|
||||
/// </summary>
|
||||
[AllowAnonymous]
|
||||
[ApiExplorerSettings(GroupName = "WeChat")]
|
||||
public class WeChatMpAppService(
|
||||
IOptions<WeChatMpOptions> options,
|
||||
IWeChatMpApiClient weChatMpApiClient,
|
||||
IWeChatEventHandler eventHandler,
|
||||
IHttpContextAccessor httpContextAccessor,
|
||||
ILogger<WeChatMpAppService> logger) : ApplicationService
|
||||
{
|
||||
/// <summary>
|
||||
/// 微信服务器接入验签(兼容 WMS 公众号菜单配置:/api/apiwechat/verification)。
|
||||
/// </summary>
|
||||
[HttpGet("/apiwechat/verification")]
|
||||
[Produces("text/plain")]
|
||||
public ContentResult ValidateLegacyAsync(
|
||||
[FromQuery] string? signature,
|
||||
[FromQuery] string? timestamp,
|
||||
[FromQuery] string? nonce,
|
||||
[FromQuery] string? echostr)
|
||||
=> ValidateCore(signature, timestamp, nonce, echostr);
|
||||
|
||||
/// <summary>
|
||||
/// 微信服务器接入验签。
|
||||
/// </summary>
|
||||
[HttpGet("/wechat/mp/{appId}")]
|
||||
[Produces("text/plain")]
|
||||
public ContentResult ValidateAsync(
|
||||
[FromRoute] string appId,
|
||||
[FromQuery] string? signature,
|
||||
[FromQuery] string? timestamp,
|
||||
[FromQuery] string? nonce,
|
||||
[FromQuery] string? echostr)
|
||||
{
|
||||
EnsureAppId(appId);
|
||||
return ValidateCore(signature, timestamp, nonce, echostr);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 微信消息/事件回调(兼容 WMS:/api/apiwechat/verification)。
|
||||
/// </summary>
|
||||
[HttpPost("/apiwechat/verification")]
|
||||
[Consumes("text/xml", "application/xml", "text/plain")]
|
||||
[Produces("application/xml")]
|
||||
public Task<ContentResult> MessageLegacyAsync(
|
||||
[FromQuery] string signature,
|
||||
[FromQuery] string timestamp,
|
||||
[FromQuery] string nonce,
|
||||
[FromQuery] string? openid,
|
||||
[FromQuery(Name = "encrypt_type")] string? encryptType,
|
||||
[FromQuery(Name = "msg_signature")] string? msgSignature,
|
||||
CancellationToken cancellationToken = default)
|
||||
=> MessageCoreAsync(signature, timestamp, nonce, encryptType, cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// 微信消息/事件回调。
|
||||
/// </summary>
|
||||
[HttpPost("/wechat/mp/{appId}")]
|
||||
[Consumes("text/xml", "application/xml", "text/plain")]
|
||||
[Produces("application/xml")]
|
||||
public async Task<ContentResult> MessageAsync(
|
||||
[FromRoute] string appId,
|
||||
[FromQuery] string signature,
|
||||
[FromQuery] string timestamp,
|
||||
[FromQuery] string nonce,
|
||||
[FromQuery] string? openid,
|
||||
[FromQuery(Name = "encrypt_type")] string? encryptType,
|
||||
[FromQuery(Name = "msg_signature")] string? msgSignature,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
EnsureAppId(appId);
|
||||
return await MessageCoreAsync(signature, timestamp, nonce, encryptType, cancellationToken);
|
||||
}
|
||||
|
||||
private ContentResult ValidateCore(
|
||||
string? signature,
|
||||
string? timestamp,
|
||||
string? nonce,
|
||||
string? echostr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.Value.Token))
|
||||
{
|
||||
throw new BusinessException("未配置微信公众号 Token");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(signature)
|
||||
|| string.IsNullOrWhiteSpace(timestamp)
|
||||
|| string.IsNullOrWhiteSpace(nonce)
|
||||
|| string.IsNullOrWhiteSpace(echostr))
|
||||
{
|
||||
return new ContentResult { Content = "error", ContentType = "text/plain; charset=utf-8" };
|
||||
}
|
||||
|
||||
var valid = WeChatSignatureHelper.Validate(options.Value.Token, timestamp, nonce, signature);
|
||||
return new ContentResult
|
||||
{
|
||||
Content = valid ? echostr : "error",
|
||||
ContentType = "text/plain; charset=utf-8"
|
||||
};
|
||||
}
|
||||
|
||||
private async Task<ContentResult> MessageCoreAsync(
|
||||
string signature,
|
||||
string timestamp,
|
||||
string nonce,
|
||||
string? encryptType,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!WeChatSignatureHelper.Validate(options.Value.Token, timestamp, nonce, signature))
|
||||
{
|
||||
logger.LogWarning("微信消息验签失败");
|
||||
return new ContentResult { Content = "error", ContentType = "text/plain; charset=utf-8" };
|
||||
}
|
||||
|
||||
var request = httpContextAccessor.HttpContext?.Request
|
||||
?? throw new BusinessException("无法读取微信回调请求");
|
||||
using var reader = new StreamReader(request.Body);
|
||||
var requestBody = await reader.ReadToEndAsync(cancellationToken);
|
||||
if (string.IsNullOrWhiteSpace(requestBody))
|
||||
{
|
||||
return new ContentResult { Content = string.Empty, ContentType = "application/xml; charset=utf-8" };
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(encryptType)
|
||||
&& !string.Equals(encryptType, "raw", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.LogWarning("收到加密微信消息({EncryptType}),当前仅支持明文模式,请在公众平台切换为明文或后续启用 AES。", encryptType);
|
||||
return new ContentResult { Content = string.Empty, ContentType = "application/xml; charset=utf-8" };
|
||||
}
|
||||
|
||||
var message = WeChatXmlMessageParser.Parse(requestBody);
|
||||
if (message == null)
|
||||
{
|
||||
return new ContentResult { Content = string.Empty, ContentType = "application/xml; charset=utf-8" };
|
||||
}
|
||||
|
||||
var reply = await eventHandler.HandleAsync(message, cancellationToken);
|
||||
return new ContentResult { Content = reply ?? string.Empty, ContentType = "application/xml; charset=utf-8" };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取 PC 扫码登录二维码。
|
||||
/// </summary>
|
||||
[HttpGet("/wechat/mp/login-qrcode")]
|
||||
public async Task<WxLoginQrcodeOutput> GetLoginQrcodeAsync(
|
||||
[FromQuery] string loginUuid,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(loginUuid))
|
||||
{
|
||||
throw new BusinessException("缺少 loginUuid");
|
||||
}
|
||||
|
||||
const int expireSeconds = 600;
|
||||
var qrCodeUrl = await weChatMpApiClient.CreateLoginQrCodeUrlAsync(loginUuid.Trim(), expireSeconds, cancellationToken);
|
||||
return new WxLoginQrcodeOutput
|
||||
{
|
||||
QrCodeUrl = qrCodeUrl,
|
||||
ExpiresIn = expireSeconds
|
||||
};
|
||||
}
|
||||
|
||||
private void EnsureAppId(string appId)
|
||||
{
|
||||
var configured = options.Value.AppId;
|
||||
if (string.IsNullOrWhiteSpace(configured))
|
||||
{
|
||||
throw new BusinessException("未配置微信公众号 AppId");
|
||||
}
|
||||
|
||||
if (!string.Equals(configured, appId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new BusinessException("公众号 AppId 不匹配");
|
||||
}
|
||||
}
|
||||
}
|
||||
97
src/SimpleAdmin.Api/WeChat/WeChatMpCallbackSupport.cs
Normal file
97
src/SimpleAdmin.Api/WeChat/WeChatMpCallbackSupport.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using Fake.Application;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SimpleAdmin.Api.WeChat.Options;
|
||||
using SimpleAdmin.Api.WeChat.Services;
|
||||
|
||||
namespace SimpleAdmin.Api.WeChat;
|
||||
|
||||
internal static class WeChatMpCallbackSupport
|
||||
{
|
||||
public static ContentResult Validate(
|
||||
IOptions<WeChatMpOptions> options,
|
||||
string? signature,
|
||||
string? timestamp,
|
||||
string? nonce,
|
||||
string? echostr)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.Value.Token))
|
||||
{
|
||||
throw new BusinessException("未配置微信公众号 Token");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(signature)
|
||||
|| string.IsNullOrWhiteSpace(timestamp)
|
||||
|| string.IsNullOrWhiteSpace(nonce)
|
||||
|| string.IsNullOrWhiteSpace(echostr))
|
||||
{
|
||||
return PlainText("error");
|
||||
}
|
||||
|
||||
var valid = WeChatSignatureHelper.Validate(options.Value.Token, timestamp, nonce, signature);
|
||||
return PlainText(valid ? echostr : "error");
|
||||
}
|
||||
|
||||
public static async Task<ContentResult> HandleMessageAsync(
|
||||
IOptions<WeChatMpOptions> options,
|
||||
IWeChatEventHandler eventHandler,
|
||||
HttpRequest request,
|
||||
ILogger logger,
|
||||
string signature,
|
||||
string timestamp,
|
||||
string nonce,
|
||||
string? encryptType,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
if (!WeChatSignatureHelper.Validate(options.Value.Token, timestamp, nonce, signature))
|
||||
{
|
||||
logger.LogWarning("微信消息验签失败");
|
||||
return PlainText("error");
|
||||
}
|
||||
|
||||
using var reader = new StreamReader(request.Body);
|
||||
var requestBody = await reader.ReadToEndAsync(cancellationToken);
|
||||
if (string.IsNullOrWhiteSpace(requestBody))
|
||||
{
|
||||
return Xml(string.Empty);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(encryptType)
|
||||
&& !string.Equals(encryptType, "raw", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
logger.LogWarning(
|
||||
"收到加密微信消息({EncryptType}),当前仅支持明文模式,请在公众平台切换为明文或后续启用 AES。",
|
||||
encryptType);
|
||||
return Xml(string.Empty);
|
||||
}
|
||||
|
||||
var message = WeChatXmlMessageParser.Parse(requestBody);
|
||||
if (message == null)
|
||||
{
|
||||
return Xml(string.Empty);
|
||||
}
|
||||
|
||||
var reply = await eventHandler.HandleAsync(message, cancellationToken);
|
||||
return Xml(reply ?? string.Empty);
|
||||
}
|
||||
|
||||
public static void EnsureAppId(IOptions<WeChatMpOptions> options, string appId)
|
||||
{
|
||||
var configured = options.Value.AppId;
|
||||
if (string.IsNullOrWhiteSpace(configured))
|
||||
{
|
||||
throw new BusinessException("未配置微信公众号 AppId");
|
||||
}
|
||||
|
||||
if (!string.Equals(configured, appId, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
throw new BusinessException("公众号 AppId 不匹配");
|
||||
}
|
||||
}
|
||||
|
||||
private static ContentResult PlainText(string content) =>
|
||||
new() { Content = content, ContentType = "text/plain; charset=utf-8" };
|
||||
|
||||
private static ContentResult Xml(string content) =>
|
||||
new() { Content = content, ContentType = "application/xml; charset=utf-8" };
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user