基于.NetCore2.1。服务类库采用.Net Standard2.0,兼容.net 4.6.1消息推送服务

https://www.cnblogs.com/ibeisha/p/weixinServer.html

关于微信公众号服务的中间件,基于.NetCore2.1。服务类库采用.Net Standard2.0,兼容.net 4.6.1。

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

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

这里,我新建了一个名为WeiXinMiddleware的类,代码如下:

复制代码

///



///

///

public class WeiXinMiddleware

{

///



///

///

private RequestDelegate Next = null;

    /// <summary>
/// <![CDATA[配置]]>
/// </summary>
public IConfiguration Configuration { get; } /// <summary>
/// <![CDATA[中间件配置信息]]>
/// </summary>
public OAuth.WeiXinServerOptions ServerOptions { get; set; } /// <summary>
/// <![CDATA[构造]]>
/// </summary>
/// <param name="requestDelegate"></param>
/// <param name="configuration"></param>
public WeiXinMiddleware(RequestDelegate requestDelegate, IConfiguration configuration, OAuth.WeiXinServerOptions serverOptions)
{
Next = requestDelegate;
Configuration = configuration;
ServerOptions = serverOptions;
} /// <summary>
/// <![CDATA[调用]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
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; }

    /// <summary>
///
/// </summary>
private IWeiXinServerProvider _ServerProvider = null; /// <summary>
/// <![CDATA[微信服务提供程序]]>
/// </summary>
public IWeiXinServerProvider WeiXinServerProvider
{
get
{
return _ServerProvider;
}
set
{
_ServerProvider = value;
_ServerProvider.ServerOptions = this;
}
} /// <summary>
/// <![CDATA[当接收到消息时]]>
/// </summary>
public Func<HttpContext, Task> OnRecieveAsync { get; set; } /// <summary>
/// <![CDATA[扫描事件]]>
/// </summary>
public Func<WeiXinContext, Task> OnScanAsync { get; set; } /// <summary>
/// <![CDATA[关注事件]]>
/// </summary>
public Func<WeiXinContext, Task> OnSubscribeAsync { get; set; } /// <summary>
/// <![CDATA[取消关注]]>
/// </summary>
public Func<WeiXinContext, Task> OnUnsubscribeAsync { get; set; } /// <summary>
/// <![CDATA[菜单点击事件]]>
/// </summary>
public Func<WeiXinContext, Task> OnClickAsync { get; set; } /// <summary>
/// <![CDATA[点击链接]]>
/// </summary>
public Func<WeiXinContext, Task> OnViewAsync { get; set; } /// <summary>
/// <![CDATA[上报地理位置]]>
/// </summary>
public Func<WeiXinContext, Task> OnLocationAsync { get; set; } /// <summary>
/// <![CDATA[被动接收普通消息]]>
/// </summary>
public Func<HttpContext, Task> OnRecieveMessageAsync { get; set; }
}

复制代码

这个类中,定义了中间件要拦截处理的URL,以及时间消息的处理委托,有了这些委托,我们就可以很灵活的实现在接收到微信推送消息后的逻辑处理。

这个类中,还定义了一个WeiXinServerProvider属性,它是接口IWeiXinServerProvider的派生,让我们看看它定义的成员吧!

复制代码

public interface IWeiXinServerProvider

{

    /// <summary>
///
/// </summary>
OAuth.WeiXinServerOptions ServerOptions { get; set; } /// <summary>
///
/// </summary>
/// <param name="context"></param>
/// <param name="configuration"></param>
/// <param name="serverOptions"></param>
/// <returns></returns>
Task Run(HttpContext context, IConfiguration configuration);
}

复制代码

很简单吧,一个属性,一个运行任务的函数。

上面几个类是我服务的核心,下面我又创建了2个扩展类,分别为添加中间件和IOC注入服务。

复制代码

///



///

///

public static class WeiXinMiddlewareExtensions

{

    /// <summary>
/// <![CDATA[]]>
/// </summary>
/// <param name="app"></param>
/// <param name="serverOptions"></param>
public static void UseWeiXinServer(this IApplicationBuilder app, OAuth.WeiXinServerOptions serverOptions)
{
app.UseMiddleware<Middleware.WeiXinMiddleware>(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<CookiePolicyOptions>(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);
} /// <summary>
///
/// </summary>
/// <param name="app"></param>
/// <param name="env"></param>
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

{

    /// <summary>
/// <![CDATA[服务选项]]>
/// </summary>
public OAuth.WeiXinServerOptions ServerOptions { get; set; } /// <summary>
///
/// </summary>
public WeiXinServer()
{ } /// <summary>
/// <![CDATA[运行服务]]>
/// </summary>
/// <param name="context"></param>
/// <param name="configuration"></param>
/// <param name="serverOptions"></param>
/// <returns></returns>
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 虚方法
/// <summary>
/// <![CDATA[虚方法,接收消息后处理]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
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);
} /// <summary>
/// <![CDATA[被动接收消息]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task OnRecieveMessage(HttpContext context)
{
if (ServerOptions.OnRecieveMessageAsync != null) return ServerOptions.OnRecieveMessageAsync(context);
return Task.Delay(0);
} /// <summary>
/// <![CDATA[扫描事件]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task OnScan(WeiXinContext context)
{
if (ServerOptions.OnScanAsync != null) return ServerOptions.OnScanAsync(context);
return Task.Delay(0);
} /// <summary>
/// <![CDATA[关注事件]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task OnSubscribe(WeiXinContext context)
{
if (ServerOptions.OnSubscribeAsync != null) return ServerOptions.OnSubscribeAsync(context);
return Task.Delay(0);
} /// <summary>
/// <![CDATA[取消关注]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task OnUnsubscribe(WeiXinContext context)
{
if (ServerOptions.OnUnsubscribeAsync != null) return ServerOptions.OnUnsubscribeAsync(context);
return Task.Delay(0);
} /// <summary>
/// <![CDATA[菜单点击]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task OnClick(WeiXinContext context)
{
if (ServerOptions.OnClickAsync != null) return ServerOptions.OnClickAsync(context);
return Task.Delay(0);
} /// <summary>
/// <![CDATA[点击菜单链接]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public virtual Task OnView(WeiXinContext context)
{
if (ServerOptions.OnViewAsync != null) return ServerOptions.OnViewAsync(context);
return Task.Delay(0);
} /// <summary>
/// <![CDATA[上报地理位置]]>
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
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 获取,不足之处,还望不吝赐教。

基于.NetCore2.1。服务类库采用.Net Standard2.0,兼容.net 4.6.1消息推送服务的更多相关文章

  1. 基于APNs最新HTTP/2接口实现iOS的高性能消息推送(服务端篇)

    1.前言 本文要分享的消息推送指的是当iOS端APP被关闭或者处于后台时,还能收到消息/信息/指令的能力. 这种在APP处于后台或关闭情况下的消息推送能力,通常在以下场景下非常有用: 1)IM即时通讯 ...

  2. 异步tcp通信——APM.Server 消息推送服务的实现

    消息推送服务 服务器推送目前流行就是私信.发布/订阅等模式,基本上都是基于会话映射,消息对列等技术实现的:高性能.分布式可以如下解决:会话映射可采用redis cluster等技术实现,消息对列可使用 ...

  3. SignalR Self Host+MVC等多端消息推送服务(1)

    一.概述 由于项目需要,最近公司项目里有个模块功能,需要使用到即时获得审批通知:原本的设计方案是使用ajax对服务器进行定时轮询查询,刚刚开始数据量和使用量不大的时候还好,后来使用量的增加和系统中各种 ...

  4. SSM项目使用GoEasy 实现web消息推送服务

      一.背景 之前项目需要做一个推送功能,最开始我用websocket实现我的功能.使用websocket的好处是免费自主开发,但是有几个问题:1)浏览器的兼容问题,尤其是低版本的ie:2)因为是推送 ...

  5. Worktile中百万级实时消息推送服务的实现

    Worktile中百万级实时消息推送服务的实现 出自:http://blog.jobbole.com/81125/

  6. SignalR Self Host+MVC等多端消息推送服务(2)

    一.概述 上次的文章中我们简单的实现了SignalR自托管的服务端,今天我们来实现控制台程序调用SignalR服务端来实现推送信息,由于之前我们是打算做审批消息推送,所以我们的demo方向是做指定人发 ...

  7. 搭建websocket消息推送服务,必须要考虑的几个问题

    近年,不论是正在快速增长的直播,远程教育以及IM聊天场景,还是在常规企业级系统中用到的系统提醒,对websocket的需求越来越大,对websocket的要求也越来越高.从早期对websocket的应 ...

  8. SignalR Self Host+MVC等多端消息推送服务(3)

    一.概述 最近项目确实太忙,而且身体也有点不舒服,慢性咽炎犯了,昨晚睡觉时喘不过气来,一直没休息好,也没什么时间写博客,今天朋友问我什么时候能出web端的消息发送的文章时,我还在忙着改项目的事,趁着中 ...

  9. SignalR Self Host+MVC等多端消息推送服务(4)

    由于工作太忙,一直没时间更新博客,之前有很多朋友一直问我什么时候将后续的代码发上来,一直没时间,今天就长话短说,不写文章了,直接上demo,里面将正式项目中用到的一些敏感信息修改了,要使用的话下载后自 ...

随机推荐

  1. html5--1.20 课程小结与若干点补充

    html5--1.20 课程小结与若干点补充 学习要点: 1.第一章HTML5基础知识做一个小结2.对本章课程中部分内容做几点补充 课程小结 对本章的知识点做一个简单的回顾,并对其中个别知识点做若干补 ...

  2. NOIP 2014【斗地主】

    这真是道大火题. 因为保证数据随机,所以开始很多人直接用搜索 + 贪心水过去了,后来,为了遏制骗分这种不良风气的传播,各大 OJ 相继推出了斗地主加强版-- 正解: 先爆搜顺子,枚举打或不打,打多少张 ...

  3. Dual Path Networks(DPN)——一种结合了ResNet和DenseNet优势的新型卷积网络结构。深度残差网络通过残差旁支通路再利用特征,但残差通道不善于探索新特征。密集连接网络通过密集连接通路探索新特征,但有高冗余度。

    如何评价Dual Path Networks(DPN)? 论文链接:https://arxiv.org/pdf/1707.01629v1.pdf在ImagNet-1k数据集上,浅DPN超过了最好的Re ...

  4. CodeForces - 592D: Super M(虚树+树的直径)

    Ari the monster is not an ordinary monster. She is the hidden identity of Super M, the Byteforces’ s ...

  5. unbuntu下安装qq

    由于Wine QQ一直没更新版本导致目前版本报版本过低无法使用,暂时先上UK官网的国际版Wine QQ,虽然功能没那么新,但稳定能用: 下载: 下载地址:http://www.ubuntukylin. ...

  6. dubbo的防痴呆设计

    项目中也经常会遇到各种因为配置而引入的问题,很多技术支持解决不掉就找开发,结果发现大部分还是配置错误或网络不通等.如果在设计之初就能考虑到并针对这些问题做出应对设计,甚至给出异常的解决方案,确实可以减 ...

  7. Mesos的quorum配置引发的问题

    Mesos安装完毕后,发现agent无法和master关联(通过WebUI的agent页面无法看到agent信息),查看日志显示: Elected as the leading master! sta ...

  8. bzoj 3073 [Pa2011]Journeys ——线段树优化连边

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3073 建两棵线段树,一棵孩子向父亲连边,是走出去的:一棵父亲向孩子连边,是走进来的. 注意第 ...

  9. HDU1875(最小生成树)

    畅通工程再续 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Subm ...

  10. H.264有四种画质级别

    H264相关知识-poseidonqiu-ChinaUnix博客 H.264有四种画质级别分别是BP.EP.MP.HP: 1.BP-Baseline Profile:基本画质.支持I/P 帧,只支持无 ...