您的位置:首页 > 编程语言 > ASP

Asp.Net Core微信服务中间件-.NetCore2.1

2018-09-02 18:44 696 查看
又封周末,闲暇无聊,随手写了一个关于微信公众号服务的中间件,基于.NetCore2.1。服务类库采用.NetStandard2.0,兼容.net4.6.1。

整体思路是,设计一个中间件,提供微信消息推送服务。目前实现了,接收微信消息推送后,根据消息类型,对事件消息和被动接收消息分别进行了处理。

在中间件和服务之间,创建一个服务提供类,拥有提供消息的处理逻辑,开发者,可以实现服务提供接口,完成自己的逻辑。下面,让我们看看关于中间件的代码设计:

这里,我新建了一个名为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上,不足之处,还望不吝赐教。



                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: