【小程序】基于.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
【小程序】基于.NET CORE2.1 的 微信开放平台 第三方平台开发 教程一 准备工作的更多相关文章
- 微信小程序-基于canvas画画涂鸦
代码地址如下:http://www.demodashi.com/demo/14461.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...
- 微信小程序基于swiper组件的tab切换
代码地址如下:http://www.demodashi.com/demo/14010.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...
- 微信小程序基于scroll-view实现锚点定位
代码地址如下:http://www.demodashi.com/demo/14009.html 一.前期准备工作 软件环境:微信开发者工具 官方下载地址:https://mp.weixin.qq.co ...
- 微信小程序-基于高德地图API实现天气组件(动态效果)
微信小程序-基于高德地图API实现天气组件(动态效果) 在社区翻腾了许久,没有找到合适的天气插件.迫不得已,只好借鉴互联网上的web项目,手动迁移到小程序中使用.现在分享到互联网社区中,帮助后续有 ...
- 微信小程序--基于ColorUI构建皮皮虾短视频去水印组件(仅供学习使用)
微信小程序--基于ColorUI构建皮皮虾短视频去水印组件(仅供学习使用) 没错,我是皮友,我想学习舞蹈(/doge)和瑜伽 ,要无水印的那种有助于我加深学习. 1.组件效果展示 2.组件引入准备 h ...
- 微信小程序 -- 基于 movable-view 实现拖拽排序
微信小程序 -- 基于 movable-view 实现拖拽排序 项目基于colorui样式组件 ColorUI组件库 (color-ui.com) 1.实现效果 2. 设计思路 movable-vie ...
- 小程序基于疼讯qcloud的nodejs开发服务器部署
腾讯,疼讯,很疼. 请慎重看腾讯给出的文档,最好做一个笔记. 我只能说我能力有限,在腾讯云小程序的文档中跳了n天. 最后还是觉得记录下来,以防止我的cpu过载给烧了. 此文档是对<小程序 ...
- [转]微信小程序之购物数量加减 —— 微信小程序实战商城系列(3)
本文转自:http://blog.csdn.net/michael_ouyang/article/details/70194144 我们在购买宝贝的时候,购物的数量,经常是我们需要使用的,如下所示: ...
- 微信小程序火爆,谁能在微信小程序赚取第一桶金?
2016年末,最火的话题:微信小程序.身边好多朋友蠢蠢欲动的想要借微信小程序创业,春节期间整理思绪,我们就简单说说微信的小程序可能会让哪些人赚钱: 1,微信小程序培训,能够快速赚钱 做培训的肯定首先赚 ...
随机推荐
- 使用HDP快速搭建Hadoop开发环境 | Debugo
本文简单记录了一下使用VMware workstation 10.CentOS和HDP 2.0.6(Hadoop 2.2)发行版构建Hadoop开发测试环境的全部流程.这个过程中我遇到了不少问题,也耽 ...
- Centos 5.11 samba
1.使用yum安装samba,添加samba user yum install samba samba-client samba-swatuseradd smbuser -m ...
- 湘潭邀请赛——Alice and Bob
Alice and Bob Accepted : 133 Submit : 268 Time Limit : 1000 MS Memory Limit : 65536 KB Problem ...
- IEnumerator<TItem>和IEnumerator Java 抽象类和普通类、接口的区别——看完你就顿悟了
IEnumerable 其原型至少可以说有15年历史,或者更长,它是通过 IEnumerator 来定义的,而后者中使用装箱的 object 方式来定义,也就是弱类型的.弱类型不但会有性能问题,最主要 ...
- Ubuntu14.04常用安装
sudo apt-get update sudo apt-get install flashplugin-nonfree ================= 类飞秋软件 sudo apt-get in ...
- 【iOS系列】-iOS开发常用库文件总结
这里是列举出得一部分,更多内容可参考https://github.com/darren90/Gather_iOS 码农周刊的总结 - 覆盖很广 调调的 - 很多开发相关内容都有体现 右滑返回的解决 - ...
- 让Linq的OrderBy支持动态字段
使用linq的OrderBy,如果明确知道是哪个字段,当然很容易: IQueryable<User> userQuery = ...; userQuery.OrderBy(u => ...
- ExtJs布局中,控件如何水平居中?
如此即可,有图有代码有j8: var formGridHead = Ext.create('Ext.form.Panel', { id: 'MyGridHead', region: 'north', ...
- Response.ContentLength获取文件大小
Response.ContentLength返回的是请求内容的大小,而不是请求文件的大小,当我们用Http断点续传的时候,比如文件大小为100M,第一次下载的时候ContentLength的大小就是1 ...
- Unable to resolve dependency for ':app@debug/compileClasspath': Could not resolve com.android.support.constraint:constraint-layout:1.1.0. Could not resolve com.android.support.constraint:constraint-l
File->Settings->Build, Execution, Deployment->Gradle->取消选中 Offline work 按钮