特此说明:本篇文章为个人原创文章,创作不易,未经作者本人同意、许可等条件,不得以任何形式搬运、转载、抄袭(等包括但不限于此手段)本文章,否则保留追究有关侵权人责任的权利

一、认识微信公众号模板消息

什么是微信公众号模板消息呢?我们先看微信官方是怎么定义的:

模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等。不支持广告等营销类消息以及其它所有可能对用户造成骚扰的消息。

以上定义对于普通人来说可能没有什么概念,下面就列举我们生活中触手可及的微信公众号模板消息的使用场景——丰巢快递柜。

大多数人都经历过网购,购买的商品在最后环节由快递员进行投递时因为种种原因,快递小哥有时会将商品放入丰巢快递柜。在商品进入丰巢快递柜且快递小哥关闭柜门后,丰巢公众号(假设此时你已经关注了该公众号)会向你推送一条快递取件消息提醒,而这条消息本身就是我们要讲的微信公众号模板消息。如下图所示:

二、接口测试号申请

由于用户体验和安全性方面的考虑,微信公众号的注册有一定门槛,某些高级接口的权限需要微信认证后才可以获取。

所以,为了帮助开发者快速了解和上手微信公众号开发,熟悉各个接口的调用,微信官方推出了微信公众帐号测试号,通过手机微信扫描二维码即可获得测试号。

本篇(系列)文章的所有介绍内容,也将基于微信公众账号测试号进行讲解,正式账号与测试账号的流程操作等相差无几甚至有的一模一样,不通之处可以进行类比参考。

进入微信公众帐号测试号申请系统。如下图所示:

点击中间的登录按钮,打开微信APP,通过微信tab右上角的扫一扫登录,登录后的界面如下图所示:

三、关注/取消关注公众号事件

在微信用户和公众号产生交互的过程中,用户的某些操作会使得微信服务器通过事件推送的形式通知到开发者在开发者中心处设置的服务器地址,从而开发者可以获取到该信息。其中,某些事件推送在发生后,是允许开发者回复用户的,某些则不允许。目录如下:

  1. 关注/取消关注事件
  2. 扫描带参数二维码事件
  3. 上报地理位置事件
  4. 自定义菜单事件
  5. 点击菜单拉取消息时的事件推送
  6. 点击菜单跳转链接时的事件推送

我们目前需要用到【关注/取消关注事件】,该事件有极大的使用用途。

用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL。方便开发者给用户下发欢迎消息或者做帐号的解绑。为保护用户数据隐私,开发者收到用户取消关注事件时需要删除该用户的所有信息。

上面提到一个URL关键词,这是非常重要的一个环节,在以后的开发中有着极为重要的用途。下面教大家如何填写URL。接入指南概述。

第一步:了解需要填写的信息

在【测试号管理】页面找到【接口配置信息】一栏,如下图:

因为我之前填写过,所以这里有填写好的信息展示。如果是第一次填写,此处内容为空白。下面解释需要填写的信息名词及作用:

1、URL:是开发者用来接收微信消息和事件的接口URL。

  • 目前该URL的请求方式为:GET。(后期需要改为POST,后面会有介绍)
  • 该URL作用:处理/响应微信发送的事件信息。
  • 该URL必须能在公网直接访问并响应消息,且不需要任何权限验证。
  • 该URL访问端口需为80端口(公众平台接口调用仅支持80端口)或者443端口
  • 该URL一种可能示例:http://{域名}:80/api/WX

2、Token:可由开发者可以任意填写,用作生成签名。(该Token会和接口URL中包含的Token进行比对,从而验证安全性)

3、EncodingAESKey(测试账号无需填写):由开发者手动填写或随机生成,将用作消息体加解密密钥。

4、消息加解密方式:此项内容在该处不多加阐述,有兴趣的小伙伴可自行上网寻找相关资料了解。

第二步:了解微信服务器发起的请求并如何进行相应

当开发者提交URL等信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

参数 描述
signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
echostr 随机字符串
timestamp 时间戳
nonce 随机数

微信服务器某次发起的真实请求示例如下:

http://{域名}:80/api/WX?signature=169fadc3ce1cee923c5089c2a6be71843f0e2484&echostr=8343114011812817647&timestamp=1646381291&nonce=496537975

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

加密/校验流程如下:

  1. 将token、timestamp、nonce三个参数进行字典序排序
  2. 将三个参数字符串拼接成一个字符串进行sha1加密
  3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

第三步:开发URL接口

直接贴上我写的代码如下:

public class WXController : Controller
{
[HttpGet]
[AllowAnonymous]
public string Index([FromQuery] WXIndexInput input)
{
return input.echostr;
}
}

Dto类WXIndexInput如下:

public class WXIndexInput
{
public string signature { get; set; } public string echostr { get; set; } public long timestamp { get; set; } public long nonce { get; set; }
}

上述代码开发完成后发布到公网服务器,并自测能成功访问并响应数据。

第四步:填写URL

  1. 在【测试号管理】页面找到【接口配置信息】一栏,并点击旁边的【修改】按钮,参见第一步中的图片。
  2. 在URL输入框中填写第三步中开发的URL接口地址,如:http://{域名}:80/api/WX
  3. 在Token输入框中填写你自定义的Token值。
  4. 最后点击提交按钮,页面弹出【配置成功】提示框,如下图。
  5. URL配置项到此全部结束。如弹出其他错误信息,请小伙伴们查阅资料自行解决。

第五步:了解用户关注/取消关注公众号时微信服务器发起的事件请求

前面我们说到,用户在关注与取消关注公众号时,微信会把这个事件推送到开发者填写的URL(也就是我们第三步中开发的URL接口,此时请求方式为POST,后面我们将对该接口进行改造)。方便开发者给用户下发欢迎消息或者做帐号的解绑。为保护用户数据隐私,开发者收到用户取消关注事件时需要删除该用户的所有信息。微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。关于重试的消息排重,推荐使用FromUserName + CreateTime 排重。假如服务器无法保证在五秒内处理并回复,可以直接回复空串,微信服务器不会对此作任何处理,并且不会发起重试。

那么微信官方发起的事件请求是什么样式的呢?下面将进行详细的讲解。

  • 微信官方服务器在用户关注/取消关注公众号时发起的事件请求的Content-Type为application/xml类型。参见如下形式:
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>

1、关注公众号

  • 微信服务器发起的一次用户关注公众号的请求URL如下:
http://{域名}:80/api/WX?signature=33345f994ce4965279ac47e0f510ad395e6efaa1&timestamp=1646391249&nonce=1845667659&openid=oLrmn5rh9480z38etS-YMCDD-cwc
  • 微信服务器发起的一次用户关注公众号的xml请求体如下:
<xml>
<ToUserName><![CDATA[gh_df27052ba1f3]]></ToUserName>
<FromUserName><![CDATA[oLrmn5rh9480z38etS-YMCDD-cwc]]></FromUserName>
<CreateTime>1646391249</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
<EventKey><![CDATA[]]></EventKey>
</xml>

2、取消关注公众号

  • 微信服务器发起的一次用户取消关注公众号的请求URL如下:
http://{域名}:80/api/WX?signature=c9223edc3e32dbed9115049d77f16e49ef5e3583&timestamp=1646391279&nonce=1449911721&openid=oLrmn5rh9480z38etS-YMCDD-cwc
  • 微信服务器发起的一次用户取消关注公众号的xml请求体如下:
<xml>
<ToUserName><![CDATA[gh_df27052ba1f3]]></ToUserName>
<FromUserName><![CDATA[oLrmn5rh9480z38etS-YMCDD-cwc]]></FromUserName>
<CreateTime>1646391279</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[unsubscribe]]></Event>
<EventKey><![CDATA[]]></EventKey>
</xml>

参数说明如下表:

参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)

第六步:改造URL接口

通过第五步真实的微信服务器发起的请求实例发现,原先第三步开发的URL接口已经无法满足现有的需求了。

接下来,我们将保留原接口输入输出不变的情况下对原接口进行重写,以满足现在的需求功能。

重写后的代码如下所示:

public class WXController : Controller
{
[HttpPost]
[AllowAnonymous]
[Produces("application/xml")]
public string Index([FromQuery] WXIndexInput input)
{
var httpRequestBody = HttpContext.Request.Body;//获取请求体
XmlDocument xmlDoc = new XmlDocument();//初始化一个XmlDocument实例
xmlDoc.Load(httpRequestBody);//从请求体流中载入xml
var node = xmlDoc.SelectSingleNode("xml");//选择与XPath表达式匹配的第一个XmlNode。
var entityModel = new WXSubOrNotXmlEntityModel();//WXSubOrNotXmlEntityModel类的属性与Http请求体的xml节点一一对应
foreach (var item in node.ChildNodes)//遍历xml节点的子节点
{
var xe = (XmlElement)item;//将子节点转换成xml元素
var p = entityModel.GetType().GetProperty(xe.Name);//获取WXSubOrNotXmlEntityModel类实例与xml元素名称相同的属性
if (p != null)
{
var text = xe.InnerText;//获取xml元素节点的文本值【string类型】
var pName = p.PropertyType.Name;//获取p属性的数据类型
if (pName == typeof(long).Name)//如果p属性的数据类型为long
{
var textLongType = long.Parse(text);//将xml元素节点的文本值【string类型】转换成long类型
p.SetValue(entityModel, textLongType);//给p属性赋值为xml元素节点的文本值
}
else
{
p.SetValue(entityModel, text);//给p属性赋值为xml元素节点的文本值
}
}
}
//至此,entityModel实例已被全部赋值为相同xml节点名称中的文本值,用此实例中的属性值就可以进行接下开的业务逻辑开发……
//比如,当判断事件是用户取消关注公众号,则删除用户在关注后绑定或者其他业务逻辑产生的绑定数据关系……
//……
return input.echostr;
}
}

Dto类WXIndexInput如下:

public class WXIndexInput
{
public string signature { get; set; } public string echostr { get; set; } public long timestamp { get; set; } public long nonce { get; set; } public string openid { get; set; }
}

上述代码修改完成后发布到公网服务器,并自测能成功访问并响应数据。

第七步:测试用户关注/取消关注公众号时URL接口是否被成功调起

在【测试号管理】页面中找到【测试号二维码】一栏,如下图所示:

扫描图中二维码关注/取消关注公众号,查看该方法被调用的系统日志,即可知道该方法是否被成功调起。

如果方法未被成功调起,请小伙伴自行排查并解决问题,直到该方法被成功调起为止,否则影响后面业务逻辑的进行。

至此,关于用户关注/取消关注公众号事件的讲解到此结束。

本篇文章未完待续……

文章后续正在创作中,敬请期待……

【C#版本】微信公众号模板消息对接(一)(图文详解)的更多相关文章

  1. 【C#版本】微信公众号模板消息对接(二)(图文详解)

    本篇文章承接上一篇文章内容,点击此段文字传送至上一篇文章. 特此说明:本篇文章为个人原创文章,创作不易,未经作者本人同意.许可等条件,不得以任何形式搬运.转载.抄袭(等包括但不限于上述手段)本文章,否 ...

  2. Java对接微信公众号模板消息推送

    内容有点多,请耐心! 最近公司的有这个业务需求,又很凑巧让我来完成: 首先想要对接,先要一个公众号,再就是开发文档了:https://developers.weixin.qq.com/doc/offi ...

  3. 微信公众号 模板消息 定时推送 java

    前提:业务需要,要做一个关于月报的微信消息推送.即每个月定时自动发送一条消息 给关注 公众号的人 用的是 公众号的测试账号(实际开发需要认证的公众号) 微信官网的 模板消息接口规则: 1.所有服务号都 ...

  4. Java开发微信公众号模板消息【同步|异步】

    第一步:申请模板消息功能并添加模板 在微信公众平台找到你需要的模板,并添加上即可: 第二步:添加功能模块后开始开发 功能中使用的类及代码: 发送数据主实体类: Template.java packag ...

  5. C#开发微信门户及应用(27)-公众号模板消息管理

    通过模板消息接口,公众号能向关注其账号的用户发送预设模板的消息.模板消息仅用于公众号向用户发送重要的服务通知,只能用于符合其要求的服务场景中,如信用卡刷卡通知,商品购买成功通知等.不支持广告等营销类消 ...

  6. 微信服务号模板消息接口新增"设置行业"和"添加模板"及细节优化

    微信服务号模板消息可以向用户发送重要的服务通知,如信用卡刷卡通知,商品购买成功通知等.昨日,微信团队发布公告称模板消息新增“设置行业”和“添加模板”接口及细节优化,详细变动如下 模板消息[业务通知]自 ...

  7. 微信小程序结合微信公众号进行消息发送

    微信小程序结合微信公众号进行消息发送 由于小程序的模板消息已经废弃了,官方让使用订阅消息功能.而订阅消息的使用限制比较大,用户必须得订阅.需要获取用户同意接收消息的权限.用户必须得和小程序有交互的时候 ...

  8. C#实现微信公众号群发消息(解决一天只能发一次的限制)

    经过几天研究网上的代码和谢灿大神的帮忙,今天终于用C#实现了微信公众号群发消息,现在整理一下. 总体思路:1.首先必须要在微信公众平台上申请一个公众号. 2.然后进行模拟登陆.(由于我对http传输原 ...

  9. 2014-07-24 .NET实现微信公众号的消息回复与自定义菜单

    今天是在吾索实习的第12天.我们在这一天中,基本实现了微信公众号的消息回复与自定义菜单的创建. 首先,是实现消息回复,其关键点如下: 读取POST来的数据流:Stream 数据流变量 = HttpCo ...

随机推荐

  1. axios请求的封装

    /* axios的请求封装 */         //axios的原生写法get,post请求         //第一个参数为请求地址,第二个参数为请求的参数,params是将参数拼接在url的后面 ...

  2. MyCms 自媒体 CMS 系统 v2.8,支持织梦数据导入

    MyCms 是一款基于Laravel开发的开源免费的自媒体博客CMS系统,助力开发者知识技能变现. MyCms 基于Apache2.0开源协议发布,免费且不限制商业使用,欢迎持续关注我们. V2.8 ...

  3. C++实现switch匹配字符串string(map方法)

    如果语法中大量使用if...else语句会造成代码臃肿,if语句C++语法中switch...case中case只能是整形变量,这里提供了一种思路,用map方法使健与值对应,这样字符串string类型 ...

  4. python29day

    内容回顾 网络编程 概念 B/S C/S架构 B/S browser server C/S client 装客户端使用的 server远程服务器的 osi七层协议 今日内容 tcp协议的编程 如何在连 ...

  5. 创建一个python类 ,self init相关参数的简单介绍

    一 创建 ''' 一 使用python 语法 创建一个类, 探究self 是干啥的 1 创建一个对象 car 2 写入两个行参 3 定义两个方法 ''' class Car(): ''' 二 init ...

  6. 如何在pyqt中使用 QStyle 重绘 QSlider

    前言 使用 qss 可以很方便地改变 QSlider 的样式,但是有些情况下 qss 无法满足我们的需求.比如下图所示样式: 如果直接使用 qss 将 handle 的内圆设置为透明背景,会看到 ha ...

  7. dotnet 替换 ASP.NET Core 的底层通讯为命名管道的 IPC 库

    这是一个用于本机多进程进行 IPC 通讯的库,此库的顶层 API 是采用 ASP.NET Core 的 MVC 框架,其底层通讯不是传统的走网络的方式,而是通过 dotnetCampus.Ipc 开源 ...

  8. BIMFACE 二次开发 SDK 之歌

    <BIMFACE SDK 之歌>讲述了作者与 BIMFACE 从相识.相知.相爱.相守的艳遇之爱唯美故事   我是一个小小的程序员 穿行在人来人往的IT行业之间 编程工作与建筑信息化相关 ...

  9. Swift循环的介绍

    循环的介绍 在开发中经常会需要循环 常见的循环有:for/while/do while. 这里我们只介绍for/while,因为for/while最常见 for循环的写法 最常规写法 // 传统写法 ...

  10. kindof

    kindof:相当于 __kindof:表示当前类或者它的子类' 类设计历史 id:可以调用任何对象方法,不能进行编译检查 @interface Person : NSObject // xcode5 ...