又封周末,闲暇无聊,随手写了一个关于微信公众号服务的中间件,基于.NetCore2.1。服务类库采用.Net Standard2.0,兼容.net 4.6.1。
整体思路是,设计一个中间件,提供微信消息推送服务。目前实现了,接收微信消息推送后,根据消息类型,对事件消息和被动接收消息分别进行了处理。
在中间件和服务之间,创建一个服务提供类,拥有提供消息的处理逻辑,开发者,可以实现服务提供接口,完成自己的逻辑。下面,让我们看看关于中间件的代码设计:
这里,我新建了一个名为WeiXinMiddleware的类,代码如下:
////// /// public class WeiXinMiddleware { ////// /// private RequestDelegate Next = null; ////// /// public IConfiguration Configuration { get; } ////// /// public OAuth.WeiXinServerOptions ServerOptions { get; set; } ////// /// /// /// public WeiXinMiddleware(RequestDelegate requestDelegate, IConfiguration configuration, OAuth.WeiXinServerOptions serverOptions) { Next = requestDelegate; Configuration = configuration; ServerOptions = serverOptions; } ////// /// /// ///public async Task Invoke(HttpContext context) {if (context.Request.Path == ServerOptions.NotifyPath) { //微信服务 if (ServerOptions.WeiXinServerProvider == null) ServerOptions.WeiXinServerProvider = (OAuth.IWeiXinServerProvider)context.RequestServices.GetService(typeof(OAuth.IWeiXinServerProvider)); await ServerOptions.WeiXinServerProvider.Run(context, Configuration); return; } await Next.Invoke(context); } }
代码其实很简单,就是在类内部定义一个Invoke任务,再声明一个Next属性,用于请求的下一步处理委托。在中间件的构造函数中,进行了注入,其中有一个
WeiXinServerOptions 类,它便是定义中间件所需的配置信息,也是对外提供的接口,让我们看看具体的代码:
////// /// public class WeiXinServerOptions { ////// /// public PathString NotifyPath { get; set; } ////// /// private IWeiXinServerProvider _ServerProvider = null; ////// /// public IWeiXinServerProvider WeiXinServerProvider { get { return _ServerProvider; } set { _ServerProvider = value; _ServerProvider.ServerOptions = this; } } ////// /// public FuncOnRecieveAsync { get; set; } /// /// /// public FuncOnScanAsync { get; set; } /// /// /// public FuncOnSubscribeAsync { get; set; } /// /// /// public FuncOnUnsubscribeAsync { get; set; } /// /// /// public FuncOnClickAsync { get; set; } /// /// /// public FuncOnViewAsync { get; set; } /// /// /// public FuncOnLocationAsync { get; set; } /// /// /// public FuncOnRecieveMessageAsync { get; set; } }
这个类中,定义了中间件要拦截处理的URL,以及时间消息的处理委托,有了这些委托,我们就可以很灵活的实现在接收到微信推送消息后的逻辑处理。
这个类中,还定义了一个WeiXinServerProvider属性,它是接口IWeiXinServerProvider的派生,让我们看看它定义的成员吧!
public interface IWeiXinServerProvider
{ ///
///
///
OAuth.WeiXinServerOptions ServerOptions { get; set; } ///
///
///
///
///
///
/// Task Run(HttpContext context, IConfiguration configuration);
}很简单吧,一个属性,一个运行任务的函数。
上面几个类是我服务的核心,下面我又创建了2个扩展类,分别为添加中间件和IOC注入服务。
////// /// public static class WeiXinMiddlewareExtensions { ////// /// /// /// public static void UseWeiXinServer(this IApplicationBuilder app, OAuth.WeiXinServerOptions serverOptions) { app.UseMiddleware(serverOptions); } }
下面是IOC注入的扩展方法:
////// /// public static class WeiXinServiceCollectionExtensions { ////// /// /// public static void AddWeiXinServer(this IServiceCollection services) { services.AddSingleton(typeof(OAuth.IWeiXinServerProvider), typeof(OAuth.WeiXinServer));//单例:IOC注册服务类型 } }
完成以上代码后,最后让我们再Start类中,进行服务的配置。
public class Startup
{ public Startup(IConfiguration configuration)
{
Configuration = configuration;
} public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure(options =>
{ // This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddWeiXinServer();//IOC注册服务类型
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
} ///
///
///
///
///
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
} else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
} //使用微信中间件
app.UseWeiXinServer(new OAuth.WeiXinServerOptions()
{
NotifyPath = new PathString("/OAuth/WeiXin"), //WeiXinServerProvider = new OAuth.WeiXinServer(),//此处也可也手动设置,默认通过IOC容器创建WeiXinServer实例。
OnScanAsync = (context) => { return Task.Delay(0); },
OnClickAsync = (context) => { return Task.Delay(0); },
OnSubscribeAsync = (context) => { return Task.Delay(0); },
OnUnsubscribeAsync = (context) => { return Task.Delay(0); },
OnViewAsync = (context) => { return Task.Delay(0); },
OnRecieveMessageAsync = (context) => { return Task.Delay(0); },
});
app.UseStaticFiles();
app.UsecookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
} 让我们再看看WeiXinServer类的定义:
////// /// public class WeiXinServer : IWeiXinServerProvider { ////// /// public OAuth.WeiXinServerOptions ServerOptions { get; set; } ////// /// public WeiXinServer() { } ////// /// /// /// /// ///public async Task Run(HttpContext context, IConfiguration configuration) { #region 1、验证签名 if (context.Request.Method.ToUpper() == "GET") { context.Response.ContentType = "text/plain;charset=utf-8"; context.Response.StatusCode = 200; //1、验证签名 if (WeiXin.Sdk.Common.Util.CheckSignature(context.Request.Query["nonce"], context.Request.Query["timestamp"], context.Request.Query["signature"], configuration.GetSection("WeiXinOAuth")["Token"])) { await context.Response.WriteAsync(context.Request.Query["echostr"]); return; } await context.Response.WriteAsync("无效签名!"); return; } #endregion 1、验证签名 #region 2、接收微信消息 await onRecieve(context);//接收消息 #endregion 2、接收微信消息 } #region 虚方法 /// /// /// /// ///public virtual Task onRecieve(HttpContext context) { if (ServerOptions.OnRecieveAsync != null) return ServerOptions.onRecieveAsync(context); string strRecieveBody = null;//接收消息 using (System.IO.StreamReader streamReader = new System.IO.StreamReader(context.Request.Body)) { strRecieveBody = streamReader.ReadToEndAsync().GetAwaiter().GetResult(); } //序列化 WeiXin.Sdk.Common.Serialization.XmlSerializer xmlSerializer = new WeiXin.Sdk.Common.Serialization.XmlSerializer(typeof(WeiXin.Sdk.Domain.Messages.Message)); var recieve = (WeiXin.Sdk.Domain.Messages.Message)xmlSerializer.Deserialize(strRecieveBody); //事件消息 if (recieve.MsgType == WeiXin.Sdk.Common.Constants.SystemConstants.MSG_TYPE.EVENT) { var weiXinContext = new WeiXinContext(recieve, context);
var weiXinContext = new WeiXinContext(recieve, context);
var actionName = recieve.Event.ToLower();
actionName = actionName.First().ToString().ToUpper() + actionName.Substring(1);
var action = this.GetType().GetMethod($"On{actionName}");
if (action != null) return (Task)action.Invoke(this, new object[] { weiXinContext });
} //被动接收消息
else
{ return onRecieveMessage(context);
} return Task.Delay(0);
} ///
///
///
///
///
public virtual Task onRecieveMessage(HttpContext context)
{ if (ServerOptions.OnRecieveMessageAsync != null) return ServerOptions.onRecieveMessageAsync(context); return Task.Delay(0);
} ///
///
///
///
///
public virtual Task onScan(WeiXinContext context)
{ if (ServerOptions.OnScanAsync != null) return ServerOptions.onScanAsync(context); return Task.Delay(0);
} ///
///
///
///
///
public virtual Task onSubscribe(WeiXinContext context)
{ if (ServerOptions.OnSubscribeAsync != null) return ServerOptions.onSubscribeAsync(context); return Task.Delay(0);
} ///
///
///
///
///
public virtual Task onUnsubscribe(WeiXinContext context)
{ if (ServerOptions.OnUnsubscribeAsync != null) return ServerOptions.onUnsubscribeAsync(context); return Task.Delay(0);
} ///
///
///
///
///
public virtual Task onClick(WeiXinContext context)
{ if (ServerOptions.OnClickAsync != null) return ServerOptions.onClickAsync(context); return Task.Delay(0);
} ///
///
///
///
///
public virtual Task onView(WeiXinContext context)
{ if (ServerOptions.OnViewAsync != null) return ServerOptions.onViewAsync(context); return Task.Delay(0);
} ///
///
///
///
///
public virtual Task onLocation(WeiXinContext context)
{ if (ServerOptions.OnLocationAsync != null) return ServerOptions.onLocationAsync(context); return Task.Delay(0);
} #endregion
}WeiXinServer类中还定义了时间消息的相关的虚方法,虚方法中,调用Options配置中定义的委托,这样,开发者一方面可以通过继承WeiXinServer或IWeiXinServerProvider接口,或通过设置Options属性,来灵活运用,开发者可根据自身需求,完成
对应业务逻辑即可。有了这些设计,我们可以轻松配置和完成微信消息的处理。
以上内容的全部代码,可以通过访问https://gitee.com/lichaoqiang/weixinmd 获取,不足之处,还望不吝赐教。
原文出处:https://www.cnblogs.com/ibeisha/p/weixinServer.html



