【小程序】基于.NET CORE2.1 的 微信开放平台 第三方平台开发 教程一 准备工作
微信第三方平台概述
公众平台第三方平台是为了让公众号或小程序运营者,在面向垂直行业需求时,可以一键授权给第三方平台(并且可以同时授权给多家第三方),通过第三方平台来完成业务,开放给所有通过开发者资质认证后的开发者使用。
这里啰嗦一下,什么是微信第三方平台,有什么作用?
官方介绍:微信第三方平台的开放,是为了让公众号或小程序运营者,在面向垂直行业需求时,可以一键登录授权给第三方的公众号或小程序运营平台,通过第三方开发者提供的公众号或小程序第三方平台来完成相关业务。
从技术上简单来说,客户无需提供技术人员对接。无需了解微信后台开发者相关配置,比如配置Appid,AppSecret,URL,Token等等很多东西,客户只需要通过扫一扫授权给第三方平台就能得到第三方平台微信授权的相关运营功能。前提是第三方平台已经完全对接微信开放平台的大部分功能。
这一篇主要是讲解如何通过.NET CORE2.1技术开发后台对接微信开放平台的小程序授权。
废话不多说,大家都知道磨刀不误砍柴工,所以建议大家都去看一遍微信开放平台文档,文档说明已经很详细了。至少自己心中要有大概的了解及思路。地址 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318292&token=&lang=
开发必备工具
IDE:VS2017
运行环境:netcoreapp2.1
数据库:Mysql
框架主要运用技术及组件:
- .NET Core 2.1
- Entity Framework Core 2.1.0
- Pomelo.EntityFrameworkCore.MySql 2.1.0-rc1-final
- Senparc.Weixin.Open 2.10.5
微信开放平台准备
1、注册微信开放平台账户,并通过企业认证。
2、进入管理中心,切换到【第三方平台】创建第一个第三方平台
第三方平台可开通微信公众号授权、小程序授权,根据贵公司的开放进度及功能开放程度进行选择。
具体步骤
在第三方平台方创建成功并最终开发测试完毕,提交全网发布申请时,微信服务器会通过自动化测试的方式,检测服务的基础逻辑是否可用,在确保基础可用的情况下,才会允许公众号或小程序第三方平台提交全网发布。
微信后台会自动将下述公众号配置为第三方平台方的一个额外的测试公众号,并通过该帐号,执行如下所述的测试步骤,第三方平台方需要根据各步骤描述的自动化测试规则实现相关逻辑,才能通过接入检测,达到全网发布的前提条件。
请注意,必须预先按照测试各步骤要求,代码实现相关逻辑后,去点击“全网发布”按钮,才有可能全网发布成功。
3、以小程序授权Demo来介绍相关参数
开发参数配置 相关参数说明 访问 https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318462&lang=zh_CN
白名单IP配置允许用户在全网未发布状态下用于在线环境开发测试。
小程序模板配置
以上就是第三方平台的基础配置信息。
4.基于.NET CORE 2.1搭建一个简单的第三方平台。
新建一个空的解决方案。主要用于第三方授权及小程序业务管理。
第三方平台与微信开放平台的业务对接我们直接使用 Senparc的Open组件
安装组件完成之后,我们需要做的是要获取微信开放平台主动推送的 component_verify_ticket
官方文档说明:
在公众号第三方平台创建审核通过后,微信服务器会向其“授权事件接收URL”每隔10分钟定时推送component_verify_ticket。第三方平台方在收到ticket推送后也需进行解密(详细请见【消息加解密接入指引】),接收到后必须直接返回字符串success。
我们建议一个控制 WxOpenController 用来接受微信开放平台POST推送的消息。
我们按照Senparc的第三方平台的Demo,先用最简单的方式保存component_verify_ticket到~/App_Data/OpenTicket/{AppId}.txt文本",当然啦也可以存入数据库或其他可以持久化的地方。
CustomThirdPartyMessageHandlers 用于接收微信开放平台推送的消息并解析。有了解过Senparc相关组件的盆友们,估计应该都知道这个类的用处,这里不多说了,详细说明看文档。
核心代码,与Senparc官网的代码暂时没多大区别。我们主要是为后面处理小程序的授权及业务做准备工作。
appsettings.json
{ "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*", "SenparcWeixinSetting": { //公众号 "Token": "***", "EncodingAESKey": "**********************", "WeixinAppId": "*********************", "WeixinAppSecret": "**************************", //开放平台 "Component_Appid": "***************************", "Component_Secret": "***********************************" } }
WxOpenController.cs
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Senparc.Weixin; using Senparc.Weixin.Entities; using Senparc.Weixin.MP.MvcExtension; using Senparc.Weixin.Open.Entities.Request; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using ZY.WxOpen.WebHost.MessageHandlers.ThirdPartyMessageHandlers; using ZY.WxOpen.WebHost.Utilities; namespace ZY.WxOpen.WebHost.Controllers { public class WxOpenController : Controller { readonly Func<string> _getRandomFileName = () => DateTime.Now.ToString("yyyyMMdd-HHmmss") + Guid.NewGuid().ToString("n").Substring(0, 6); //微信的全局配置,根据Senparc的官网Demo进行配置 private readonly SenparcWeixinSetting _senparcWeixinSetting; //第三方平台的APPid private string componentAppid; //第三方平台的配置的token private string token; //第三方平台的配置的加密key private string encodingAESKey; private readonly IHostingEnvironment _env; public WxOpenController(IHostingEnvironment env, IOptions<SenparcWeixinSetting> senparcWeixinSetting) { _env = env; _senparcWeixinSetting = senparcWeixinSetting.Value; componentAppid = _senparcWeixinSetting.Component_Appid; token = _senparcWeixinSetting.Token; encodingAESKey = _senparcWeixinSetting.EncodingAESKey; } [ActionName("Index")] public ActionResult Index() { return Content("测试"); } [HttpPost] [ActionName("Index")] public ActionResult Post(PostModel postModel/*,[FromBody]string requestXml*/) { #region 打包 PostModel 信息 postModel.AppId = componentAppid; postModel.Token = token;//根据自己后台的设置保持一致 postModel.EncodingAESKey = encodingAESKey;//根据自己后台的设置保持一致 #endregion //配置日志输出 var logPath = Server.GetMapPath(string.Format("~/App_Data/Open/{0}/", DateTime.Now.ToString("yyyy-MM-dd"))); if (!Directory.Exists(logPath)) { Directory.CreateDirectory(logPath); } string body = new StreamReader(Request.Body).ReadToEnd(); byte[] requestData = Encoding.UTF8.GetBytes(body); Stream inputStream = new MemoryStream(requestData); try { var messageHandler = new CustomThirdPartyMessageHandler(inputStream, postModel); #region 记录 Request 日志 //测试时可开启此记录,帮助跟踪数据,使用前请确保App_Data文件夹存在,且有读写权限。 var requestDocumentFileName = Path.Combine(logPath, string.Format("{0}_Request_{1}.txt", _getRandomFileName(), messageHandler.RequestMessage.AppId)); var ecryptRequestDocumentFileName = Path.Combine(logPath, string.Format("{0}_Request_Ecrypt_{1}.txt", _getRandomFileName(), messageHandler.RequestMessage.AppId)); using (FileStream fs = new FileStream(requestDocumentFileName, FileMode.CreateNew, FileAccess.ReadWrite)) { messageHandler.RequestDocument.Save(fs); } using (FileStream fs = new FileStream(ecryptRequestDocumentFileName, FileMode.CreateNew, FileAccess.ReadWrite)) { messageHandler.EcryptRequestDocument.Save(fs); } #endregion //执行微信处理过程 messageHandler.Execute(); #region 记录 Response 日志 //测试时可开启,帮助跟踪数据 //if (messageHandler.ResponseDocument == null) //{ // throw new Exception(messageHandler.RequestDocument.ToString()); //} var responseDocumentFileName = Path.Combine(logPath, string.Format("{0}_Response_{1}.txt", _getRandomFileName(), messageHandler.RequestMessage.AppId)); var ecryptResponseDocumentFileName = Path.Combine(logPath, string.Format("{0}_Response_Final_{1}.txt", _getRandomFileName(), messageHandler.RequestMessage.AppId)); if (messageHandler.ResponseMessageText != null) { using (FileStream fs = new FileStream(responseDocumentFileName, FileMode.CreateNew, FileAccess.ReadWrite)) { byte[] bytes = Encoding.UTF8.GetBytes(messageHandler.ResponseMessageText); try { //设定书写的开始位置为文件的末尾 fs.Position = fs.Length; //将待写入内容追加到文件末尾 fs.Write(bytes, 0, bytes.Length); } catch (Exception ex) { Console.WriteLine("文件打开失败{0}", ex.ToString()); } } } #endregion` return new FixWeixinBugWeixinResult(messageHandler.ResponseMessageText);//为了解决官方微信5.0软件换行bug暂时添加的方法,平时用下面一个方法即可 } catch (Exception ex) { #region 异常处理 WeixinTrace.Log("MessageHandler错误:{0}", ex.Message); using (var fs = new FileStream(Server.GetMapPath("~/App_Data/Error_" + _getRandomFileName() + ".txt"), FileMode.CreateNew, FileAccess.ReadWrite)) { using (TextWriter tw = new StreamWriter(fs)) { tw.WriteLine("ExecptionMessage:" + ex.Message); tw.WriteLine(ex.Source); tw.WriteLine(ex.StackTrace); //tw.WriteLine("InnerExecptionMessage:" + ex.InnerException.Message); tw.WriteLine("ExecptionMessage:" + ex.ToString()); if (ex.InnerException != null) { tw.WriteLine("========= InnerException ========="); tw.WriteLine(ex.InnerException.Message); tw.WriteLine(ex.InnerException.Source); tw.WriteLine(ex.InnerException.StackTrace); } tw.Flush(); //tw.Close(); } } return Content(""); #endregion } } } }
CustomThirdPartyMessageHandler.cs
using Senparc.Weixin.Open; using System.IO; using Senparc.Weixin; using Senparc.Weixin.Open.Entities.Request; using ZY.WxOpen.WebHost.Utilities; namespace ZY.WxOpen.WebHost.MessageHandlers.ThirdPartyMessageHandlers { /// <summary> /// 主要用于消息的处理 /// </summary> public class CustomThirdPartyMessageHandler : ThirdPartyMessageHandler { public CustomThirdPartyMessageHandler(Stream inputStream, PostModel encryptPostModel) : base(inputStream, encryptPostModel) { } /// <summary> /// 接收微信开放平台每10分钟推送的ComponentVerifyTicket,注意:ComponentVerifyTicket很重要 /// </summary> /// <param name="requestMessage"></param> /// <returns></returns> public override string OnComponentVerifyTicketRequest(RequestMessageComponentVerifyTicket requestMessage) { var openTicketPath = Server.GetMapPath("~/App_Data/OpenTicket"); if (!Directory.Exists(openTicketPath)) { Directory.CreateDirectory(openTicketPath); } //记录ComponentVerifyTicket(也可以存入数据库或其他可以持久化的地方) using (FileStream fs = new FileStream(Path.Combine(openTicketPath, string.Format("{0}.txt", RequestMessage.AppId)), FileMode.OpenOrCreate, FileAccess.ReadWrite)) { using (TextWriter tw = new StreamWriter(fs)) { tw.Write(requestMessage.ComponentVerifyTicket); tw.Flush(); //tw.Close(); } } return base.OnComponentVerifyTicketRequest(requestMessage); } /// <summary> /// 授权取消 /// </summary> /// <param name="requestMessage"></param> /// <returns></returns> public override string OnUnauthorizedRequest(RequestMessageUnauthorized requestMessage) { WeixinTrace.SendCustomLog("提示", $"授权取消" + requestMessage.AuthorizerAppid); //如果需要同步用户是否取消授权,可在此接收信息并进行取消授权业务处理 //取消授权 return base.OnUnauthorizedRequest(requestMessage); } } }
通过以上两个核心的类,拿到微信开放平台的大门钥匙ComponentVerifyTicket,准备工作完成了,后续就是我们需要做的业务对接。
下一篇会讲解授权及业务对接。
小程序或者公众号授权给第三方平台的技术实现流程比较简单,以公众号为例,如下图所示:
如感兴趣请多关注或者点击链接加入群聊【微信小程序】:https://jq.qq.com/?_wv=1027&k=5PnrL3m 或 搜QQ群号:397185987
作者:疯狂蚂蚁
技术交流群:306818375
出处:http://www.cnblogs.com/CrazyAnts/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。