Asp.Net Core微信服务中间件-.NetCore2.1
2018-09-02 18:44
696 查看
又封周末,闲暇无聊,随手写了一个关于微信公众号服务的中间件,基于.NetCore2.1。服务类库采用.NetStandard2.0,兼容.net4.6.1。
整体思路是,设计一个中间件,提供微信消息推送服务。目前实现了,接收微信消息推送后,根据消息类型,对事件消息和被动接收消息分别进行了处理。
在中间件和服务之间,创建一个服务提供类,拥有提供消息的处理逻辑,开发者,可以实现服务提供接口,完成自己的逻辑。下面,让我们看看关于中间件的代码设计:
这里,我新建了一个名为WeiXinMiddleware的类,代码如下:
代码其实很简单,就是在类内部定义一个Invoke任务,再声明一个Next属性,用于请求的下一步处理委托。在中间件的构造函数中,进行了注入,其中有一个
这个类中,定义了中间件要拦截处理的URL,以及时间消息的处理委托,有了这些委托,我们就可以很灵活的实现在接收到微信推送消息后的逻辑处理。
这个类中,还定义了一个WeiXinServerProvider属性,它是接口IWeiXinServerProvider的派生,让我们看看它定义的成员吧!
很简单吧,一个属性,一个运行任务的函数。
上面几个类是我服务的核心,下面我又创建了2个扩展类,分别为添加中间件和IOC注入服务。
下面是IOC注入的扩展方法:
完成以上代码后,最后让我们再Start类中,进行服务的配置。
让我们再看看WeiXinServer类的定义:
varweiXinContext=newWeiXinContext(recieve,context);
varactionName=recieve.Event.ToLower();
actionName=actionName.First().ToString().ToUpper()+actionName.Substring(1);
varaction=this.GetType().GetMethod($"On{actionName}");
if(action!=null)return(Task)action.Invoke(this,newobject[]{weiXinContext});
WeiXinServer类中还定义了时间消息的相关的虚方法,虚方法中,调用Options配置中定义的委托,这样,开发者一方面可以通过继承WeiXinServer或IWeiXinServerProvider接口,或通过设置Options属性,来灵活运用,开发者可根据自身需求,完成
对应业务逻辑即可。有了这些设计,我们可以轻松配置和完成微信消息的处理。
以上内容的全部代码,稍后会放在github上,不足之处,还望不吝赐教。
整体思路是,设计一个中间件,提供微信消息推送服务。目前实现了,接收微信消息推送后,根据消息类型,对事件消息和被动接收消息分别进行了处理。
在中间件和服务之间,创建一个服务提供类,拥有提供消息的处理逻辑,开发者,可以实现服务提供接口,完成自己的逻辑。下面,让我们看看关于中间件的代码设计:
这里,我新建了一个名为WeiXinMiddleware的类,代码如下:
///<summary> ///<![CDATA[微信中间件]]> ///</summary> publicclassWeiXinMiddleware { ///<summary> /// ///</summary> privateRequestDelegateNext=null; ///<summary> ///<![CDATA[配置]]> ///</summary> publicIConfigurationConfiguration{get;} ///<summary> ///<![CDATA[中间件配置信息]]> ///</summary> publicOAuth.WeiXinServerOptionsServerOptions{get;set;} ///<summary> ///<![CDATA[构造]]> ///</summary> ///<paramname="requestDelegate"></param> ///<paramname="configuration"></param> publicWeiXinMiddleware(RequestDelegaterequestDelegate,IConfigurationconfiguration,OAuth.WeiXinServerOptionsserverOptions) { Next=requestDelegate; Configuration=configuration; ServerOptions=serverOptions; } ///<summary> ///<![CDATA[调用]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicasyncTaskInvoke(HttpContextcontext) {if(context.Request.Path==ServerOptions.NotifyPath) { //微信服务 if(ServerOptions.WeiXinServerProvider==null)ServerOptions.WeiXinServerProvider=(OAuth.IWeiXinServerProvider)context.RequestServices.GetService(typeof(OAuth.IWeiXinServerProvider)); awaitServerOptions.WeiXinServerProvider.Run(context,Configuration); return; } awaitNext.Invoke(context); } }
代码其实很简单,就是在类内部定义一个Invoke任务,再声明一个Next属性,用于请求的下一步处理委托。在中间件的构造函数中,进行了注入,其中有一个
WeiXinServerOptions类,它便是定义中间件所需的配置信息,也是对外提供的接口,让我们看看具体的代码:
///<summary> /// ///</summary> publicclassWeiXinServerOptions { ///<summary> ///<![CDATA[微信通知地址]]> ///</summary> publicPathStringNotifyPath{get;set;} ///<summary> /// ///</summary> privateIWeiXinServerProvider_ServerProvider=null; ///<summary> ///<![CDATA[微信服务提供程序]]> ///</summary> publicIWeiXinServerProviderWeiXinServerProvider { get { return_ServerProvider; } set { _ServerProvider=value; _ServerProvider.ServerOptions=this; } } ///<summary> ///<![CDATA[当接收到消息时]]> ///</summary> publicFunc<HttpContext,Task>OnRecieveAsync{get;set;} ///<summary> ///<![CDATA[扫描事件]]> ///</summary> publicFunc<WeiXinContext,Task>OnScanAsync{get;set;} ///<summary> ///<![CDATA[关注事件]]> ///</summary> publicFunc<WeiXinContext,Task>OnSubscribeAsync{get;set;} ///<summary> ///<![CDATA[取消关注]]> ///</summary> publicFunc<WeiXinContext,Task>OnUnsubscribeAsync{get;set;} ///<summary> ///<![CDATA[菜单点击事件]]> ///</summary> publicFunc<WeiXinContext,Task>OnClickAsync{get;set;} ///<summary> ///<![CDATA[点击链接]]> ///</summary> publicFunc<WeiXinContext,Task>OnViewAsync{get;set;} ///<summary> ///<![CDATA[上报地理位置]]> ///</summary> publicFunc<WeiXinContext,Task>OnLocationAsync{get;set;} ///<summary> ///<![CDATA[被动接收普通消息]]> ///</summary> publicFunc<HttpContext,Task>OnRecieveMessageAsync{get;set;} }
这个类中,定义了中间件要拦截处理的URL,以及时间消息的处理委托,有了这些委托,我们就可以很灵活的实现在接收到微信推送消息后的逻辑处理。
这个类中,还定义了一个WeiXinServerProvider属性,它是接口IWeiXinServerProvider的派生,让我们看看它定义的成员吧!
publicinterfaceIWeiXinServerProvider { ///<summary> /// ///</summary> OAuth.WeiXinServerOptionsServerOptions{get;set;} ///<summary> /// ///</summary> ///<paramname="context"></param> ///<paramname="configuration"></param> ///<paramname="serverOptions"></param> ///<returns></returns> TaskRun(HttpContextcontext,IConfigurationconfiguration); }
很简单吧,一个属性,一个运行任务的函数。
上面几个类是我服务的核心,下面我又创建了2个扩展类,分别为添加中间件和IOC注入服务。
///<summary> ///<![CDATA[微信中间件扩展]]> ///</summary> publicstaticclassWeiXinMiddlewareExtensions { ///<summary> ///<![CDATA[]]> ///</summary> ///<paramname="app"></param> ///<paramname="serverOptions"></param> publicstaticvoidUseWeiXinServer(thisIApplicationBuilderapp,OAuth.WeiXinServerOptionsserverOptions) { app.UseMiddleware<Middleware.WeiXinMiddleware>(serverOptions); } }
下面是IOC注入的扩展方法:
///<summary> /// ///</summary> publicstaticclassWeiXinServiceCollectionExtensions { ///<summary> /// ///</summary> ///<paramname="services"></param> publicstaticvoidAddWeiXinServer(thisIServiceCollectionservices) { services.AddSingleton(typeof(OAuth.IWeiXinServerProvider),typeof(OAuth.WeiXinServer));//单例:IOC注册服务类型 } }
完成以上代码后,最后让我们再Start类中,进行服务的配置。
publicclassStartup { publicStartup(IConfigurationconfiguration) { Configuration=configuration; } publicIConfigurationConfiguration{get;} //Thismethodgetscalledbytheruntime.Usethismethodtoaddservicestothecontainer. publicvoidConfigureServices(IServiceCollectionservices) { services.Configure<CookiePolicyOptions>(options=> { //Thislambdadetermineswhetheruserconsentfornon-essentialcookiesisneededforagivenrequest. options.CheckConsentNeeded=context=>true; options.MinimumSameSitePolicy=SameSiteMode.None; }); services.AddWeiXinServer();//IOC注册服务类型 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } ///<summary> /// ///</summary> ///<paramname="app"></param> ///<paramname="env"></param> publicvoidConfigure(IApplicationBuilderapp,IHostingEnvironmentenv) { if(env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } //使用微信中间件 app.UseWeiXinServer(newOAuth.WeiXinServerOptions() { NotifyPath=newPathString("/OAuth/WeiXin"), //WeiXinServerProvider=newOAuth.WeiXinServer(),//此处也可也手动设置,默认通过IOC容器创建WeiXinServer实例。 OnScanAsync=(context)=>{returnTask.Delay(0);}, OnClickAsync=(context)=>{returnTask.Delay(0);}, OnSubscribeAsync=(context)=>{returnTask.Delay(0);}, OnUnsubscribeAsync=(context)=>{returnTask.Delay(0);}, OnViewAsync=(context)=>{returnTask.Delay(0);}, OnRecieveMessageAsync=(context)=>{returnTask.Delay(0);}, }); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseMvc(routes=> { routes.MapRoute( name:"default", template:"{controller=Home}/{action=Index}/{id?}"); }); } }
让我们再看看WeiXinServer类的定义:
///<summary> ///<![CDATA[微信服务]]> ///</summary> publicclassWeiXinServer:IWeiXinServerProvider { ///<summary> ///<![CDATA[服务选项]]> ///</summary> publicOAuth.WeiXinServerOptionsServerOptions{get;set;} ///<summary> /// ///</summary> publicWeiXinServer() { } ///<summary> ///<![CDATA[运行服务]]> ///</summary> ///<paramname="context"></param> ///<paramname="configuration"></param> ///<paramname="serverOptions"></param> ///<returns></returns> publicasyncTaskRun(HttpContextcontext,IConfigurationconfiguration) { #region1、验证签名 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"])) { awaitcontext.Response.WriteAsync(context.Request.Query["echostr"]); return; } awaitcontext.Response.WriteAsync("无效签名!"); return; } #endregion1、验证签名 #region2、接收微信消息 awaitOnRecieve(context);//接收消息 #endregion2、接收微信消息 } #region虚方法 ///<summary> ///<![CDATA[虚方法,接收消息后处理]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicvirtualTaskOnRecieve(HttpContextcontext) { if(ServerOptions.OnRecieveAsync!=null)returnServerOptions.OnRecieveAsync(context); stringstrRecieveBody=null;//接收消息 using(System.IO.StreamReaderstreamReader=newSystem.IO.StreamReader(context.Request.Body)) { strRecieveBody=streamReader.ReadToEndAsync().GetAwaiter().GetResult(); } //序列化 WeiXin.Sdk.Common.Serialization.XmlSerializerxmlSerializer=newWeiXin.Sdk.Common.Serialization.XmlSerializer(typeof(WeiXin.Sdk.Domain.Messages.Message)); varrecieve=(WeiXin.Sdk.Domain.Messages.Message)xmlSerializer.Deserialize(strRecieveBody); //事件消息 if(recieve.MsgType==WeiXin.Sdk.Common.Constants.SystemConstants.MSG_TYPE.EVENT) { varweiXinContext=newWeiXinContext(recieve,context);
varweiXinContext=newWeiXinContext(recieve,context);
varactionName=recieve.Event.ToLower();
actionName=actionName.First().ToString().ToUpper()+actionName.Substring(1);
varaction=this.GetType().GetMethod($"On{actionName}");
if(action!=null)return(Task)action.Invoke(this,newobject[]{weiXinContext});
} //被动接收消息 else { returnOnRecieveMessage(context); } returnTask.Delay(0); } ///<summary> ///<![CDATA[被动接收消息]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicvirtualTaskOnRecieveMessage(HttpContextcontext) { if(ServerOptions.OnRecieveMessageAsync!=null)returnServerOptions.OnRecieveMessageAsync(context); returnTask.Delay(0); } ///<summary> ///<![CDATA[扫描事件]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicvirtualTaskOnScan(WeiXinContextcontext) { if(ServerOptions.OnScanAsync!=null)returnServerOptions.OnScanAsync(context); returnTask.Delay(0); } ///<summary> ///<![CDATA[关注事件]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicvirtualTaskOnSubscribe(WeiXinContextcontext) { if(ServerOptions.OnSubscribeAsync!=null)returnServerOptions.OnSubscribeAsync(context); returnTask.Delay(0); } ///<summary> ///<![CDATA[取消关注]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicvirtualTaskOnUnsubscribe(WeiXinContextcontext) { if(ServerOptions.OnUnsubscribeAsync!=null)returnServerOptions.OnUnsubscribeAsync(context); returnTask.Delay(0); } ///<summary> ///<![CDATA[菜单点击]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicvirtualTaskOnClick(WeiXinContextcontext) { if(ServerOptions.OnClickAsync!=null)returnServerOptions.OnClickAsync(context); returnTask.Delay(0); } ///<summary> ///<![CDATA[点击菜单链接]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicvirtualTaskOnView(WeiXinContextcontext) { if(ServerOptions.OnViewAsync!=null)returnServerOptions.OnViewAsync(context); returnTask.Delay(0); } ///<summary> ///<![CDATA[上报地理位置]]> ///</summary> ///<paramname="context"></param> ///<returns></returns> publicvirtualTaskOnLocation(WeiXinContextcontext) { if(ServerOptions.OnLocationAsync!=null)returnServerOptions.OnLocationAsync(context); returnTask.Delay(0); } #endregion }
WeiXinServer类中还定义了时间消息的相关的虚方法,虚方法中,调用Options配置中定义的委托,这样,开发者一方面可以通过继承WeiXinServer或IWeiXinServerProvider接口,或通过设置Options属性,来灵活运用,开发者可根据自身需求,完成
对应业务逻辑即可。有了这些设计,我们可以轻松配置和完成微信消息的处理。
以上内容的全部代码,稍后会放在github上,不足之处,还望不吝赐教。
相关文章推荐
- ASP.NET Core 中的SEO优化(1):中间件实现服务端静态化缓存
- Asp.Net Core 通过中间件防止图片盗链
- 学习 ASP.NET Core 2.1:集成测试中使用 WebApplicationFactory
- 如何一秒钟从头构建一个 ASP.NET Core 中间件
- ASP.NET Core - 中间件与管道(1)
- ASP.NET Core中间件(Middleware)实现WCF SOAP服务端解析
- ASP.NET Core 1.0 静态文件、路由、自定义中间件、身份验证简介
- ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
- 详解Asp.Net Core 2.1+的视图缓存(响应缓存)
- 基于 ASP.NET Core 2.1 的 Razor Class Library 实现自定义错误页面的公用类库
- ASP.NET Core 使用 URL Rewrite 中间件实现 HTTP 重定向到 HTTPS
- ASP.NET Core集成微信登录
- ASP.NET Core应用的错误处理[2]:DeveloperExceptionPageMiddleware中间件如何呈现“开发者异常页面”
- ASP.NET Core真实管道详解[1]:中间件是个什么东西?
- 用VS2012建立core2.1网站项目后引用Microsoft.AspNetCore.Session不了
- (8)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Ocelot网关(Api GateWay)
- (11)学习笔记 ) ASP.NET CORE微服务 Micro-Service ---- Thrift高效通讯 (完结)
- 我眼中的ASP.NET Core之微服务