开源中文网

您的位置: 首页 > 编程开发 > 微信开发 > 正文

微信公众号开发之基础梳理

来源: 萧十一郎的博客  作者: 萧十一郎

前言

微信公众号开发已经不在是新鲜事了,不过相信也有很多小伙伴和我一样,没有接触过或者刚刚接触不久。再此,记录下学习经历和一些比较坑的注意点。

 

论介绍

首先,微信公众号分两种类型和两种情况,两种类型分别是订阅号和服务号,两种情况是,已认证或未认证的公众号。他们分别具体的接口权限大家可以在微信后台的“开发者中心-接口权限”看到。

显示“未获得”的接口状态,就是需要订阅号或服务号微信认证之后才有拥有的权限。

简单说,未认证的订阅号只有基础支持、自动回复和发送回复消息的接口,只有实现一些较简单的数据交互功能,

 

那么,什么是接口?微信接口又是什么意思?

打个比方,如果一家公司是一个系统,创业之初,你可能一个人身兼多职,同时负责营销、财务、研发等,发展到了一定规模后为了提高公司整体的运行效率和服务水平,你就需要聘用不同的人才来帮你承担相应的一部分工作,那么同时,你也需要向这部分人提供对应的工作权限以满足他们的工作需要,这就类似于接口的概念。

 

微信接口简单说,是微信公众平台向第三方开发者提供的一个用户资源数据及功能的访问/使用权限。通过对开发者提供开放统一的API接口环境,来帮助微信第三方开发者访问微信公众平台的功能和资源,以达到充分自由的运用微信公众平台用户资源及功能的目的。

在微信上,要实现这一目的,需要开启“服务器配置”如下图所示:

如图所示:大家需要注意的是,一旦开启了服务器配置,微信平台自带的自动回复、自定义菜单等功能全部失效,所有的事件推送都会被转发到你所配置的服务器地址URL上。

 

简单解释下开发者ID和服务器配置各参数的作用:

AppID是应用ID,也就是微信开发者编号的意思,在微信中主要用于创建微信菜单等。

AppSecret是应用密匙,与AppID是相配合的,意思可理解为这是私密的应用编号。

URL服务器地址是微信服务器像开发者服务器推送消息和事件的地址,可理解为你家的住址。这里没什么特别要求,无论你使用什么语言开发,只要能通过HTTP服务的80端口返回符合微信要求的XML信息即可。

Token令牌可理解为用来验证安全接头暗号,让微信服务器知道对方就是我要找的人。

EncodingAESKey可理解为暗语加密交流,以免你与用户之间的交互信息被第三方获取到后泄露敏感数据

基本原理

微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,然后微信服务器将请求转发给自定义服务(也就是开发者服务器,url对应的服务器)。

服务处理完毕,然后回发给微信服务器,微信服务器再将具体响应回复到终端。

通信协议为:HTTP

数据格式为:XML

具体的流程如下图所示:

                                          

其实,我们需要做的事情,就是对HTTP请求,做出响应。

具体的请求内容,我们按照特定的XML格式去解析,处理完毕后,也要按照特定的XML格式返回。

我们只需要一个简单的实现HttpHandler即可。

当然,微信平台还能实现更加复杂的业务,比如微信可以作为内嵌的浏览器,我们可以通过微信的链接,打开htm界面,然后实现自己的逻辑。

当普通微信用户向公众账号发消息时,微信服务器将POST该消息到填写的URL上。结构如下

 

 业务流程

 前面的注册及消息接入就不过多赘述了,上面我们已经初步讲解了微信公众账号开发的基本原理,这里我们来探索设计实现

主要功能介绍如下:

1)请求接口层。处理HTTP请求,及响应

2)分发层。由接口层传入请求,然后具体分析请求类型,分发至不同的处理器

3)业务逻辑层。这里是我们的具体业务逻辑了,根据请求,实现具体的业务逻辑。

4)数据层。我们在实现某个应用时可能需要访问数据,可以是数据库或者是文件。如果是简单应用,可能没有这一层。

其实,具体的应用可以在这个结构上去扩展,可以扩展消息对象层、业务对象层、数据访问层、功能管理层等。这里只是提供一种思路,不局限于此。

根据层次图,设计流程图,具体讲述实现的各个过程。以便了解整个处理过程。如下图所示:

根据流程图,我们能够清晰的了解整个流程,消息处理的具体实现步骤。

下面我们针对流程进行代码实现。

具体实现

 

一、接收HTTP请求

 此处我是以MVC做演示,新建了一个MVC项目,右键新建一个一般应用程序,如下图所示

一般应用程序里面代码:

复制代码
    public void ProcessRequest(HttpContext context)
        {
            context.Response.ContentType = "text/plain";
            //context.Response.Write("Hello World");

            //由微信服务接收请求,具体处理请求
            WeiXinService wxService = new WeiXinService(context.Request);
            string responseMsg = wxService.Response();
            context.Response.Clear();
            context.Response.Charset = "UTF-8";
            context.Response.Write(responseMsg);
            context.Response.End();

        }
复制代码

二、分发请求

为了能功能封装,我们也将此封装在了处理组件中。其实可以放置在一般应用程序中处理的。 

 1)验证签名

 如果是首次请求,需要验证签名。就相当于一次HTTP握手。之前在上一章中,设置的服务器URL以及token值,这个功能就是检验是否链接成功。

这个请求是GET请求。以下具体说明(官方):

业务逻辑:

加密/校验流程:

<1> 将token、timestamp、nonce三个参数进行字典序排序

<2> 将三个参数字符串拼接成一个字符串进行SHA1加密

 <3> 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信 

而官方只提供了PHP的代码示例,很多东西在C#中并非直译既得。所以这里面也有一些具体处理。先看官方的代码:

复制代码
private function checkSignature()
    {
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];    
                
        $token = TOKEN;
        $tmpArr = array($token, $timestamp, $nonce);
        sort($tmpArr);
        $tmpStr = implode( $tmpArr );
        $tmpStr = sha1( $tmpStr );
        
        if( $tmpStr == $signature ){
            return true;
        }else{
            return false;
        }
    }
复制代码

我们将其翻译成C#版本:

复制代码
         
        /// <summary>
        /// 检查签名
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        private bool CheckSignature()
        {
            string signature = Request.QueryString[SIGNATURE];
            string timestamp = Request.QueryString[TIMESTAMP];
            string nonce = Request.QueryString[NONCE];

            List<string> list = new List<string>();
            list.Add(TOKEN);
            list.Add(timestamp);
            list.Add(nonce);
            //排序
            list.Sort();
            //拼串
            string input = string.Empty;
            foreach (var item in list)
            {
                input += item;
            }
            //加密
            string new_signature = SecurityUtility.SHA1Encrypt(input);
            //验证
            if (new_signature == signature)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
复制代码

这里需要SHA1加密,具体的算法如下:

复制代码
/// <summary>
        /// SHA1加密
        /// </summary>
        /// <param name="intput">输入字符串</param>
        /// <returns>加密后的字符串</returns>
        public static string SHA1Encrypt(string intput)
        {
            byte[] StrRes = Encoding.Default.GetBytes(intput);
            HashAlgorithm mySHA = new SHA1CryptoServiceProvider();
            StrRes = mySHA.ComputeHash(StrRes);
            StringBuilder EnText = new StringBuilder();
            foreach (byte Byte in StrRes)
            {
                EnText.AppendFormat("{0:x2}", Byte);
            }
            return EnText.ToString();
        }
复制代码

2)分发请求

接下来就是具体的消息请求了,这里都是POST请求。

因为有多种消息类型,我们通过工厂类来进行封装,然后每种消息都有专门的处理器来进行处理。具体实现逻辑

复制代码
/// <summary>
        /// 处理请求
        /// </summary>
        /// <returns></returns>
        private string ResponseMsg()
        {
            string requestXml = Common.ReadRequest(this.Request);
            IHandler handler = HandlerFactory.CreateHandler(requestXml);
            if (handler != null)
            {
                return handler.HandleRequest();
            }

            return string.Empty;
        }
复制代码

处理请求的对外方法(一般应用程序中调用的方法就是这个了),即:

复制代码
/// <summary>
        /// 处理请求,产生响应
        /// </summary>
        /// <returns></returns>
        public string Response()
        {
            string method = Request.HttpMethod.ToUpper();
            //验证签名
            if (method == "GET")
            {
                if (CheckSignature())
                {
                    return Request.QueryString[ECHOSTR];
                }
                else
                {
                    return "error";
                }
            }

            //处理消息
            if (method == "POST")
            {
                return ResponseMsg();
            }
            else
            {
                return "无法处理";
            }
        }
复制代码

三、消息处理器具体处理消息

1)消息类型

请求的消息类型有哪些?回复的消息类型有哪些等。

千万要注意,请求的消息是文本类型,回复的消息,不一定也是文本哦,可以是图文、音乐等任意一种可回复的消息。具体见下表所示。

 

2)根据具体的消息接口,设计消息类。

这里给出类图,供参考。

3)针对不同的消息,会有不同的处理器,来看下具体的类图。

 

4)具体业务处理 

每个handler里面就是可以处理具体请求。输入的什么消息,访问那些数据,调用服务等,都在这里处理。

还是建议大家对具体的业务进行单独封装,在Handler中,只提供调用的接口。

因为随着业务的增加,一个Handler可能要处理很多业务,如果所有的操作逻辑都写在这里,势必影响阅读,也不易于维护与扩展。 

5)产生回复消息

在处理完请求后,需要生成回复消息,响应到终端。消息格式,就是我们介绍那些消息类型,但必须是可用于回复的,当前支持的有:文本、图文、音乐等。

一定要明确:回复的消息类型不一定要与请求的消息类型一样,比如,请求是文本,回复的可以是图文、音乐。

产生回复消息的过程,其实,就是特定的消息对象格式化为对应的XML的过程,然后将XML响应至微信服务器。

6)实例

这里以微信用户关注公众账号,然后服务端处理处理事件请求,登记用户,并提示欢迎信息。

复制代码
class EventHandler : IHandler
    {
        /// <summary>
        /// 请求的xml
        /// </summary>
        private string RequestXml { get; set; }
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="requestXml"></param>
        public EventHandler(string requestXml)
        {
            this.RequestXml = requestXml;
        }
        /// <summary>
        /// 处理请求
        /// </summary>
        /// <returns></returns>
        public string HandleRequest()
        {
            string response = string.Empty;
            EventMessage em = EventMessage.LoadFromXml(RequestXml);
            if (em.Event == EventType.Subscribe)
            {
                //注册用户
                User user = new User();
                user.OpenID = em.FromUserName;
                UserManager.Regester(user);

                //回复欢迎消息
                TextMessage tm = new TextMessage();
                tm.ToUserName = em.FromUserName;
                tm.FromUserName = em.ToUserName;
                tm.CreateTime = Common.GetNowTime();
                tm.Content = "欢迎您关注我们,我是服务小二。有事儿您说话~";
                response = tm.GenerateContent();
            }
            return response;
        }
    }
复制代码

消息推送

当普通微信用户向公众账号发消息时,微信服务器将POST该消息到填写的URL上。结构如下

文本消息

复制代码
 <xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName> 
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[this is a test]]></Content>
 <MsgId>1234567890123456</MsgId>
 </xml>
复制代码
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType text
Content 文本消息内容
MsgId 消息id,64位整型

图片消息

复制代码
<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>1348831860</CreateTime>
 <MsgType><![CDATA[image]]></MsgType>
 <PicUrl><![CDATA[this is a url]]></PicUrl>
 <MsgId>1234567890123456</MsgId>
 </xml>
复制代码
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType image
PicUrl 图片链接
MsgId 消息id,64位整型

地理位置消息

复制代码
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[location]]></MsgType>
<Location_X>23.134521</Location_X>
<Location_Y>113.358803</Location_Y>
<Scale>20</Scale>
<Label><![CDATA[位置信息]]></Label>
<MsgId>1234567890123456</MsgId>
</xml> 
复制代码
参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType location
Location_X 地理位置纬度
Location_Y 地理位置经度
Scale 地图缩放大小
Label 地理位置信息
MsgId 消息id,64位整型

链接消息

复制代码
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>1351776360</CreateTime>
<MsgType><![CDATA[link]]></MsgType>
<Title><![CDATA[公众平台官网链接]]></Title>
<Description><![CDATA[公众平台官网链接]]></Description>
<Url><![CDATA[url]]></Url>
<MsgId>1234567890123456</MsgId>
</xml> 
复制代码
参数 描述
ToUserName 接收方微信号
FromUserName 发送方微信号,若为普通用户,则是一个OpenID
CreateTime 消息创建时间
MsgType 消息类型,link
Title 消息标题
Description 消息描述
Url 消息链接
MsgId 消息id,64位整型

事件推送

事件推送只支持微信4.5版本,目前开启自定义菜单接口事件推送、关注与取消关注事件推送。其余功能即将开放,敬请期待。

复制代码
<xml><ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[EVENT]]></Event>
<EventKey><![CDATA[EVENTKEY]]></EventKey>
</xml>
复制代码
参数 描述
ToUserName 接收方微信号
FromUserName 发送方微信号,若为普通用户,则是一个OpenID
CreateTime 消息创建时间
MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)、CLICK(自定义菜单点击事件)
EventKey 事件KEY值,与自定义菜单接口中KEY值对应

消息回复

对于每一个POST请求,开发者在响应包中返回特定xml结构,对该消息进行响应(现支持回复文本、图文、语音、视频、音乐)。

微信服务器在五秒内收不到响应会断掉连接。

回复xml结构如下:

回复文本消息

复制代码
<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>12345678</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[content]]></Content>
 </xml>
复制代码
参数 描述
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间
MsgType text
Content 回复的消息内容,长度不超过2048字节

回复音乐消息

复制代码
<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>12345678</CreateTime>
 <MsgType><![CDATA[music]]></MsgType>
 <Music>
 <Title><![CDATA[TITLE]]></Title>
 <Description><![CDATA[DESCRIPTION]]></Description>
 <MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
 <HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
 </Music>
 </xml>
复制代码
参数 描述
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间
MsgType music
MusicUrl 音乐链接
HQMusicUrl 高质量音乐链接,WIFI环境优先使用该链接播放音乐

回复图文消息

复制代码
<xml>
 <ToUserName><![CDATA[toUser]]></ToUserName>
 <FromUserName><![CDATA[fromUser]]></FromUserName>
 <CreateTime>12345678</CreateTime>
 <MsgType><![CDATA[news]]></MsgType>
 <ArticleCount>2</ArticleCount>
 <Articles>
 <item>
 <Title><![CDATA[title1]]></Title> 
 <Description><![CDATA[description1]]></Description>
 <PicUrl><![CDATA[picurl]]></PicUrl>
 <Url><![CDATA[url]]></Url>
 </item>
 <item>
 <Title><![CDATA[title]]></Title>
 <Description><![CDATA[description]]></Description>
 <PicUrl><![CDATA[picurl]]></PicUrl>
 <Url><![CDATA[url]]></Url>
 </item>
 </Articles>
 </xml> 
复制代码
参数 描述
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间
MsgType news
ArticleCount 图文消息个数,限制为10条以内
Articles 多条图文消息信息,默认第一个item为大图
Title 图文消息标题
Description 图文消息描述
PicUrl 图片链接,支持JPG、PNG格式,较好的效果为大图640*320,小图80*80。
Url 点击图文消息跳转链接
 

更多详细内容请参考官网开发文档:官方开发文档

 

 最终效果

当新用户关注的时候,首先会收到Http请求,之后根据上文中提到的相关步骤即可完成操作,对于调试操作可参考第一篇文章:微信公众号开发之VS远程调试

 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 下一篇我们将介绍如何自定义文本消息回复和自定义菜单栏,敬请期待吧~

Tags:公众 基础
关于开源中文网 - 联系我们 - 广告服务 - 网站地图 - 版权声明