基於.NetCore2.1。服務類庫采用.Net Standard2.0,兼容.net 4.6.1消息推送服務


基於.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 獲取,不足之處,還望不吝賜教。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM