使用web api开发微信公众号,调用图灵机器人接口



【整理】使用web api开发微信公众号,调用图灵机器人接口(一)

此文将分两篇讲解,主要分为以下几步

  1. 签名校验;
  2. 首次提交验证申请;
  3. 接收消息;
  4. 被动响应消息(返回XML);
  5. 映射图灵消息及微信消息;

其实图灵机器人搭载微信公众号很简单,只需要把图灵的地址配到公众后台就可以了。 不过这样做之后也就没有任何扩展的可能了,因此自己实现一套!

一、签名校验

在开发者首次提交验证申请时,微信服务器将发送GET请求到填写的URL上,并且带上四个参数(signature、timestamp、nonce、echostr),开发者通过对签名(即signature)的效验,来判断此条消息的真实性。

此后,每次开发者接收用户消息的时候,微信也都会带上前面三个参数(signature、timestamp、nonce)访问开发者设置的URL,开发者依然通过对签名的效验判断此条消息的真实性。效验方式与首次提交验证申请一致。

根据微信开发者平台中的描述,我们在首次提交验证申请及接收用户消息时,都需要校验签名以确保消息来源真实。

参与签名的参数为timestampnoncetoken(即开发者中心中配置的Token令牌)

加密/校验流程如下:
1. 将token、timestamp、nonce三个参数进行字典序排序(此处注意:是三个参数的值,而不是按参数名排序) 
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

由于这个东西在接收消息时是通用的,我们可以使用授权过滤器AuthorizeAttribute来实现。

 

[xml]  view plain  copy
  1. using System.Configuration;  
  2. using System.Net;  
  3. using System.Net.Http;  
  4. using System.Web;  
  5. using System.Web.Http;  
  6. using System.Linq;  
  7. using System.Web.Http.Controllers;  
  8.   
  9. using Efh.Core.Security;  
  10.   
  11. namespace Efh.Blog.Web.Areas.WeiXin.Filter  
  12. {  
  13.     public class WXAuthorizeAttribute : AuthorizeAttribute  
  14.     {  
  15.         /// <summary>  
  16.         /// 签名Key  
  17.         /// </summary>  
  18.         private string _wxToken = ConfigurationManager.AppSettings["WXToken"];  
  19.   
  20.         /// <summary>  
  21.         /// 是否通过授权  
  22.         /// </summary>  
  23.         /// <param name="actionContext">上下文</param>  
  24.         /// <returns>是否成功</returns>  
  25.         protected override bool IsAuthorized(HttpActionContext actionContext)  
  26.         {  
  27.             var requestQueryPairs = actionContext.Request.GetQueryNameValuePairs().ToDictionary(k => k.Key, v => v.Value);  
  28.             if (requestQueryPairs.Count == 0  
  29.                 || !requestQueryPairs.ContainsKey("timestamp")  
  30.                 || !requestQueryPairs.ContainsKey("signature")  
  31.                 || !requestQueryPairs.ContainsKey("nonce"))  
  32.             {  
  33.                 return false;  
  34.             }  
  35.   
  36.             string[] waitEncryptParamsArray = new[] { _wxToken, requestQueryPairs["timestamp"], requestQueryPairs["nonce"] };  
  37.   
  38.             string waitEncryptParamStr = string.Join("", waitEncryptParamsArray.OrderBy(m => m));  
  39.   
  40.             string encryptStr = HashAlgorithm.SHA1(waitEncryptParamStr);  
  41.   
  42.             return encryptStr.ToLower().Equals(requestQueryPairs["signature"].ToLower());  
  43.         }  
  44.   
  45.         /// <summary>  
  46.         /// 处理未授权请求  
  47.         /// </summary>  
  48.         /// <param name="actionContext">上下文</param>  
  49.         protected sealed override void HandleUnauthorizedRequest(HttpActionContext actionContext)  
  50.         {  
  51.             actionContext.Response = actionContext.Request.CreateResponse(  
  52.                 HttpStatusCode.Unauthorized, new { status = "sign_error" });  
  53.         }  
  54.     }  
  55. }  
 

 

将该特性声明在我们的微信Controller或者Action上,我们的签名校验便完成了。

二、首次提交验证申请

首次提交验证申请,微信服务器来调的时候是Get请求,而且要求我们将echostr原样返回。 注意,是原样返回。不是XML,也不是Json,<string>echostr</string>和"echostr"都是不行的!

囊中羞涩,本人使用的是虚拟主机搭载在原有的项目中,故新建微信区域(WeiXin)来实现。WeiXinAreaRegistration.cs文件如下:

 

[xml]  view plain  copy
  1. public class WeiXinAreaRegistration : AreaRegistration  
  2. {  
  3.     public override string AreaName  
  4.     {  
  5.         get  
  6.         {  
  7.             return "WeiXin";  
  8.         }  
  9.     }  
  10.   
  11.     public override void RegisterArea(AreaRegistrationContext context)  
  12.     {  
  13.         context.Routes.MapHttpRoute(  
  14.             "WeiXinProcessor",  
  15.             "WeiXin/{controller}",  
  16.             new { controller = "Processor" }  
  17.         );  
  18.     }  
  19. }  
 

 

新建Processor控制器,实现如下:

[xml]  view plain  copy
  1. [WXAuthorize]  
  2. public class ProcessorController : ApiController  
  3. {  
  4.     public HttpResponseMessage Get()  
  5.     {  
  6.         var requestQueryPairs = Request.GetQueryNameValuePairs().ToDictionary(k => k.Key, v => v.Value);  
  7.   
  8.         return new HttpResponseMessage(HttpStatusCode.OK)  
  9.         {  
  10.             Content = new StringContent(requestQueryPairs["echostr"]),  
  11.         };  
  12.     }  
  13. }  

 

上述我们便实现了首次微信的验证。

三、接收消息

微信将请求的消息分为六种:文本消息、图片消息、语音消息、视频消息、地理位置消息、链接消息,其实我们还可以将事件推送也理解为其中一种。

将响应的消息分为六种: 1. 回复文本消息 2. 回复图片消息 3. 回复语音消息 4. 回复视频消息 5. 回复音乐消息 6. 回复图文消息 。我们在这儿主要使用文本消息和图文消息。

分析后我们发现,ToUserNameFromUserNameCreateTimeMsgType是所有消息共有的参数。同时也是我们响应时必需的参数。

我们创建消息基类和消息类型枚举如下

[xml]  view plain  copy
  1. public class BaseMsg  
  2. {  
  3.     public string ToUserName { get; set; }  
  4.   
  5.     public string FromUserName { get; set; }  
  6.   
  7.     public long CreateTime { get; set; }  
  8.   
  9.     public MsgType MsgType { get; set; }  
  10. }  
  11.   
  12. public enum MsgType  
  13. {  
  14.     [XmlEnum("event")]  
  15.     Event,  
  16.     [XmlEnum("text")]  
  17.     Text,  
  18.     [XmlEnum("image")]  
  19.     Image,  
  20.     [XmlEnum("voice")]  
  21.     Voice,  
  22.     [XmlEnum("video")]  
  23.     Video,  
  24.     [XmlEnum("music")]  
  25.     Music,  
  26.     [XmlEnum("news")]  
  27.     News  
  28. }  

 

此处枚举字段标注的XmlEnum稍候解释。

而后按照各消息类型的非共有的参数,分别创建对应消息的实体类

文本消息:

[xml]  view plain  copy
  1. [XmlRoot("xml")]  
  2. public class TextMsg : BaseMsg  
  3. {  
  4.     public string Content { get; set; }  
  5. }  

 图文消息:

[xml]  view plain  copy
  1. [XmlRoot("xml")]  
  2. public class NewsMsg : BaseMsg  
  3. {  
  4.     public int ArticleCount { get; set; }  
  5.   
  6.     [XmlArray("Articles")]  
  7.     [XmlArrayItem("item")]  
  8.     public List<NewsInfo> Articles { get; set; }  
  9. }  
  10.   
  11. public class NewsInfo  
  12. {  
  13.     public string Title { get; set; }  
  14.   
  15.     public string Description { get; set; }  
  16.   
  17.     public string PicUrl { get; set; }  
  18.   
  19.     public string Url { get; set; }  
  20. }  

 等等。

接下来我们就可以开始接收微信的消息了

微信是通过Post,从正文中以XML的格式将参数传递过来的

[xml]  view plain  copy
  1. var requestContent = Request.Content.ReadAsStreamAsync().Result;  

 将正文参数读取出来后,转为Xml

[xml]  view plain  copy
  1. XmlDocument xmlDoc = new XmlDocument();  
  2. xmlDoc.Load(requestContent);  

 这样,我们便可以读取到我们需要的内容了

[xml]  view plain  copy
  1. string msgTypeStr = xmlDoc.SelectSingleNode("xml/MsgType").InnerText;//消息类型  
  2. string userName = xmlDoc.SelectSingleNode("xml/FromUserName").InnerText;//来源用户标识  
  3. string efhName = xmlDoc.SelectSingleNode("xml/ToUserName").InnerText;//我们的用户标识  

 而后,我们根据消息类型,进行进一步的处理。

转下一篇

第二篇:使用web api开发微信公众号,调用图灵机器人接口(二)

原文链接:http://efenghuo.com/blog/2498.aspx

版权声明:欢迎注明出处的转载行为 https://blog.csdn.net/hyz301/article/details/64130443

【整理】使用web api开发微信公众号,调用图灵机器人接口(一)

此文将分两篇讲解,主要分为以下几步

  1. 签名校验;
  2. 首次提交验证申请;
  3. 接收消息;
  4. 被动响应消息(返回XML);
  5. 映射图灵消息及微信消息;

其实图灵机器人搭载微信公众号很简单,只需要把图灵的地址配到公众后台就可以了。 不过这样做之后也就没有任何扩展的可能了,因此自己实现一套!

一、签名校验

在开发者首次提交验证申请时,微信服务器将发送GET请求到填写的URL上,并且带上四个参数(signature、timestamp、nonce、echostr),开发者通过对签名(即signature)的效验,来判断此条消息的真实性。

此后,每次开发者接收用户消息的时候,微信也都会带上前面三个参数(signature、timestamp、nonce)访问开发者设置的URL,开发者依然通过对签名的效验判断此条消息的真实性。效验方式与首次提交验证申请一致。

根据微信开发者平台中的描述,我们在首次提交验证申请及接收用户消息时,都需要校验签名以确保消息来源真实。

参与签名的参数为timestampnoncetoken(即开发者中心中配置的Token令牌)

加密/校验流程如下:
1. 将token、timestamp、nonce三个参数进行字典序排序(此处注意:是三个参数的值,而不是按参数名排序) 
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

由于这个东西在接收消息时是通用的,我们可以使用授权过滤器AuthorizeAttribute来实现。

 

[xml]  view plain  copy
  1. using System.Configuration;  
  2. using System.Net;  
  3. using System.Net.Http;  
  4. using System.Web;  
  5. using System.Web.Http;  
  6. using System.Linq;  
  7. using System.Web.Http.Controllers;  
  8.   
  9. using Efh.Core.Security;  
  10.   
  11. namespace Efh.Blog.Web.Areas.WeiXin.Filter  
  12. {  
  13.     public class WXAuthorizeAttribute : AuthorizeAttribute  
  14.     {  
  15.         /// <summary>  
  16.         /// 签名Key  
  17.         /// </summary>  
  18.         private string _wxToken = ConfigurationManager.AppSettings["WXToken"];  
  19.   
  20.         /// <summary>  
  21.         /// 是否通过授权  
  22.         /// </summary>  
  23.         /// <param name="actionContext">上下文</param>  
  24.         /// <returns>是否成功</returns>  
  25.         protected override bool IsAuthorized(HttpActionContext actionContext)  
  26.         {  
  27.             var requestQueryPairs = actionContext.Request.GetQueryNameValuePairs().ToDictionary(k => k.Key, v => v.Value);  
  28.             if (requestQueryPairs.Count == 0  
  29.                 || !requestQueryPairs.ContainsKey("timestamp")  
  30.                 || !requestQueryPairs.ContainsKey("signature")  
  31.                 || !requestQueryPairs.ContainsKey("nonce"))  
  32.             {  
  33.                 return false;  
  34.             }  
  35.   
  36.             string[] waitEncryptParamsArray = new[] { _wxToken, requestQueryPairs["timestamp"], requestQueryPairs["nonce"] };  
  37.   
  38.             string waitEncryptParamStr = string.Join("", waitEncryptParamsArray.OrderBy(m => m));  
  39.   
  40.             string encryptStr = HashAlgorithm.SHA1(waitEncryptParamStr);  
  41.   
  42.             return encryptStr.ToLower().Equals(requestQueryPairs["signature"].ToLower());  
  43.         }  
  44.   
  45.         /// <summary>  
  46.         /// 处理未授权请求  
  47.         /// </summary>  
  48.         /// <param name="actionContext">上下文</param>  
  49.         protected sealed override void HandleUnauthorizedRequest(HttpActionContext actionContext)  
  50.         {  
  51.             actionContext.Response = actionContext.Request.CreateResponse(  
  52.                 HttpStatusCode.Unauthorized, new { status = "sign_error" });  
  53.         }  
  54.     }  
  55. }  
 

 

将该特性声明在我们的微信Controller或者Action上,我们的签名校验便完成了。

二、首次提交验证申请

首次提交验证申请,微信服务器来调的时候是Get请求,而且要求我们将echostr原样返回。 注意,是原样返回。不是XML,也不是Json,<string>echostr</string>和"echostr"都是不行的!

囊中羞涩,本人使用的是虚拟主机搭载在原有的项目中,故新建微信区域(WeiXin)来实现。WeiXinAreaRegistration.cs文件如下:

 

[xml]  view plain  copy
  1. public class WeiXinAreaRegistration : AreaRegistration  
  2. {  
  3.     public override string AreaName  
  4.     {  
  5.         get  
  6.         {  
  7.             return "WeiXin";  
  8.         }  
  9.     }  
  10.   
  11.     public override void RegisterArea(AreaRegistrationContext context)  
  12.     {  
  13.         context.Routes.MapHttpRoute(  
  14.             "WeiXinProcessor",  
  15.             "WeiXin/{controller}",  
  16.             new { controller = "Processor" }  
  17.         );  
  18.     }  
  19. }  
 

 

新建Processor控制器,实现如下:

[xml]  view plain  copy
  1. [WXAuthorize]  
  2. public class ProcessorController : ApiController  
  3. {  
  4.     public HttpResponseMessage Get()  
  5.     {  
  6.         var requestQueryPairs = Request.GetQueryNameValuePairs().ToDictionary(k => k.Key, v => v.Value);  
  7.   
  8.         return new HttpResponseMessage(HttpStatusCode.OK)  
  9.         {  
  10.             Content = new StringContent(requestQueryPairs["echostr"]),  
  11.         };  
  12.     }  
  13. }  

 

上述我们便实现了首次微信的验证。

三、接收消息

微信将请求的消息分为六种:文本消息、图片消息、语音消息、视频消息、地理位置消息、链接消息,其实我们还可以将事件推送也理解为其中一种。

将响应的消息分为六种: 1. 回复文本消息 2. 回复图片消息 3. 回复语音消息 4. 回复视频消息 5. 回复音乐消息 6. 回复图文消息 。我们在这儿主要使用文本消息和图文消息。

分析后我们发现,ToUserNameFromUserNameCreateTimeMsgType是所有消息共有的参数。同时也是我们响应时必需的参数。

我们创建消息基类和消息类型枚举如下

[xml]  view plain  copy
  1. public class BaseMsg  
  2. {  
  3.     public string ToUserName { get; set; }  
  4.   
  5.     public string FromUserName { get; set; }  
  6.   
  7.     public long CreateTime { get; set; }  
  8.   
  9.     public MsgType MsgType { get; set; }  
  10. }  
  11.   
  12. public enum MsgType  
  13. {  
  14.     [XmlEnum("event")]  
  15.     Event,  
  16.     [XmlEnum("text")]  
  17.     Text,  
  18.     [XmlEnum("image")]  
  19.     Image,  
  20.     [XmlEnum("voice")]  
  21.     Voice,  
  22.     [XmlEnum("video")]  
  23.     Video,  
  24.     [XmlEnum("music")]  
  25.     Music,  
  26.     [XmlEnum("news")]  
  27.     News  
  28. }  

 

此处枚举字段标注的XmlEnum稍候解释。

而后按照各消息类型的非共有的参数,分别创建对应消息的实体类

文本消息:

[xml]  view plain  copy
  1. [XmlRoot("xml")]  
  2. public class TextMsg : BaseMsg  
  3. {  
  4.     public string Content { get; set; }  
  5. }  

 图文消息:

[xml]  view plain  copy
  1. [XmlRoot("xml")]  
  2. public class NewsMsg : BaseMsg  
  3. {  
  4.     public int ArticleCount { get; set; }  
  5.   
  6.     [XmlArray("Articles")]  
  7.     [XmlArrayItem("item")]  
  8.     public List<NewsInfo> Articles { get; set; }  
  9. }  
  10.   
  11. public class NewsInfo  
  12. {  
  13.     public string Title { get; set; }  
  14.   
  15.     public string Description { get; set; }  
  16.   
  17.     public string PicUrl { get; set; }  
  18.   
  19.     public string Url { get; set; }  
  20. }  

 等等。

接下来我们就可以开始接收微信的消息了

微信是通过Post,从正文中以XML的格式将参数传递过来的

[xml]  view plain  copy
  1. var requestContent = Request.Content.ReadAsStreamAsync().Result;  

 将正文参数读取出来后,转为Xml

[xml]  view plain  copy
  1. XmlDocument xmlDoc = new XmlDocument();  
  2. xmlDoc.Load(requestContent);  

 这样,我们便可以读取到我们需要的内容了

[xml]  view plain  copy
  1. string msgTypeStr = xmlDoc.SelectSingleNode("xml/MsgType").InnerText;//消息类型  
  2. string userName = xmlDoc.SelectSingleNode("xml/FromUserName").InnerText;//来源用户标识  
  3. string efhName = xmlDoc.SelectSingleNode("xml/ToUserName").InnerText;//我们的用户标识  

 而后,我们根据消息类型,进行进一步的处理。

转下一篇

第二篇:使用web api开发微信公众号,调用图灵机器人接口(二)

原文链接:http://efenghuo.com/blog/2498.aspx

版权声明:欢迎注明出处的转载行为 https://blog.csdn.net/hyz301/article/details/64130443
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值