zoukankan      html  css  js  c++  java
  • Asp.Net Web API开发微信后台

    如果说用Asp.Net开发微信后台是非主流,那么Asp.Net Web API的微信后台绝对是不走寻常路。

    需要说明的是,本人认为Asp.Net Web API在开发很多不同的请求方法的Restful服务的时候是利器,可在开发微信后台的时候,因为微信调用我们这个后台的时候来来去去就一个方法,所以Web API有点杀鸡用牛刀的感觉。

    而且由于Web API其实是微软封装了大量的类库,所以会导致后台相当臃肿。所以,不建议Asp.Net Web API开发微信后台

    如果好奇心太强实在想试一下,可以参看以下内容。

    首先登陆微信公众平台,在左侧栏底下点击开发者中心填写服务器配置。

    目前仅支持80端口,所以发布的时候需要在iis配置清楚。

    创建一个Web API项目,然后添加controller,命名为:WechatController。

    在WechatController类下添加以下一个方法:

            [HttpGet]                                        //标明HttpGet注解属性明确声明这个方法仅仅接受Get请求
            [ActionName("getMsg")]                                //ActionName注解属性覆盖了方法名,这个方法名将会映射到“/api/wechat/getmsg”(如果没有修改默认的路由规则的话)
            public HttpResponseMessage WetChatVerify(HttpRequestMessage content)     //HttpRequestMessage 和HttpResponseMessage,分别用于封装Requset和Response
            {
                string echostr = (from kvp in content.GetQueryNameValuePairs()
                                  where kvp.Key == "echostr"
                                  select kvp.Value).FirstOrDefault();
    
                string signature = (from kvp in content.GetQueryNameValuePairs()
                                    where kvp.Key == "signature"
                                    select kvp.Value).FirstOrDefault();
    
                string timestamp = (from kvp in content.GetQueryNameValuePairs()
                                    where kvp.Key == "timestamp"
                                    select kvp.Value).FirstOrDefault();
    
                string nonce = (from kvp in content.GetQueryNameValuePairs()
                                where kvp.Key == "nonce"
                                select kvp.Value).FirstOrDefault();
    
                string returnStr="";
                if (string.IsNullOrEmpty(echostr) | string.IsNullOrEmpty(signature) | string.IsNullOrEmpty(timestamp) | string.IsNullOrEmpty(nonce))
                {
                    returnStr="error";
                }
    
                if (CheckSignature(signature, timestamp, nonce))
                {
                    log.Info("验证成功,返回:" + echostr);
                    returnStr=echostr;
                }
    
                HttpResponseMessage result = new HttpResponseMessage();
                result.Content = new StringContent(returnStr);
                return result;
            }    

     CheckSignature的代码如下:

            /// <summary>
            /// 验证微信签名
            /// </summary>
            private bool CheckSignature(string signature, string timestamp, string nonce)
            {
                String[] ArrTmp = { Common.Common.Token, timestamp, nonce };
    
                Array.Sort(ArrTmp);
                String tmpStr = String.Join("", ArrTmp);
    
                tmpStr = System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1").ToLower();
    
                if (tmpStr == signature)
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }    

    微信将会发送一个请求到后台,这个请求根据你在其开发平台填写的信息,类型可以是如下:

    http://【server】/api/wechat/getMsg?signature=【signature】&echostr=【echostr】&timestamp=【timestamp】&nonce=【nonce】

    只要按照CheckSignature的步骤将Token以及获取到的timestamp,nonce串联并用sha1加密,如果所得结果跟signature一致,就原样返回echostr,如此微信开发平台就会认为服务器配置成功了。

    我们知道Web API的返回类型很灵活,可以返回string类型也可以int类型,当然也可以复杂一点采用上面的HttpResponseMessage类型,不过如果返回的是string类型,就有两个问题:

    1、echostr不能原样返回,会自动给你加上双引号,所以微信开发平台会一直说验证失败,取巧一点的话,因为现在的echostr都是数字,可以返回整形;

    2、返回的文字信息如果需要换行,直接返回string类型的话,“ ”就不会当作是换行符处理而回直接在消息中显示出来。这关乎下面的一些内容了……

    验证成功之后,再写另外一个方法来接收并处理信息。

            [HttpPost]
            [ActionName("getMsg")]
            public HttpResponseMessage HandleMsgFromWeChat(HttpRequestMessage request)
            {
                List<KeyValuePair<string, string>> query = Request.GetQueryNameValuePairs().ToList();
                string xmlContent = request.Content.ReadAsStringAsync().Result;
           
    string response = string.Empty; WeChatMessage msg = WeChatHelper.GetWxMessage(xmlContent); if (msg.MsgType.Trim() == "text")//用户发送一些文字信息 { response = “oh my lady gaga”; } if (msg.MsgType.Trim() == "event")//点击菜单或者新增/取消关注 { switch (msg.EventName.Trim().ToLower()) { case "click":      //点击菜单 response = "haha"; break; case "subscribe":    //用户新增关注(可以返回一些欢迎信息之类)                                   response = “wawa”; break; case "unsubscribe":   //用户取消关注(一般不需要去返回什么信息) default: break; } } HttpResponseMessage result = new HttpResponseMessage(); result.Content = new StringContent(response); return result; }
        //自定义一个微信消息实体类
        public class WeChatMessage
        {
            public string FromUserName { get; set; }
            public string ToUserName { get; set; }
            public string MsgType { get; set; }
            public string EventName { get; set; }
            public string EventKey { get; set; }
            public string Content { get; set; }
        }
    
        //发送图文消息的列表项
        public class ArticleItem
        {
            public string title { get; set; }
            public string description { get; set; }
            public string picurl { get; set; }
            public string url { get; set; }
        }
    
        public class WeChatHelper
        {
            /// <summary>
            /// 获取微信信息。
            /// </summary>
            /// <returns></returns>
            public static WeChatMessage GetWxMessage(string xmlStr)
            {
                WeChatMessage wx = new WeChatMessage();
                XmlDocument xml = new XmlDocument();
                xml.LoadXml(xmlStr);
                wx.ToUserName = xml.SelectSingleNode("xml").SelectSingleNode("ToUserName").InnerText;
                wx.FromUserName = xml.SelectSingleNode("xml").SelectSingleNode("FromUserName").InnerText;
                wx.MsgType = xml.SelectSingleNode("xml").SelectSingleNode("MsgType").InnerText;
                if (wx.MsgType.Trim() == "text")
                {
                    wx.Content = xml.SelectSingleNode("xml").SelectSingleNode("Content").InnerText;
                }
                if (wx.MsgType.Trim() == "event")
                {
                    wx.EventName = xml.SelectSingleNode("xml").SelectSingleNode("Event").InnerText;
                    wx.EventKey = xml.SelectSingleNode("xml").SelectSingleNode("EventKey").InnerText;
                }
                return wx;
            }
    
            /// <summary>
            /// 发送文字消息
            /// </summary>
            public static string SendTextMessage(string fromUserName,string toUserName,string content)
            {
                StringBuilder sb = new StringBuilder();
    
                sb.AppendFormat("<xml><ToUserName><![CDATA[{0}]]></ToUserName>", fromUserName);
                sb.AppendFormat("<FromUserName><![CDATA[{0}]]></FromUserName>", toUserName);
                sb.AppendFormat("<CreateTime>{0}</CreateTime>", DateTime.Now);
                sb.Append("<MsgType><![CDATA[text]]></MsgType>");
                sb.AppendFormat("<Content><![CDATA[{0}]]></Content>", content);
                sb.Append("<FuncFlag>0</FuncFlag></xml>");
    
                return sb.ToString();
            }
    
            /// <summary>
            /// 发送图文列表信息,如果列表为空,会转为发送“没有搜索到内容”的文字信息
            /// </summary>
            public static string SendImageListMessage(string fromUserName, string toUserName, List<ArticleItem> itemList)
            {
                if (itemList == null || itemList.Count == 0)
                {
                    return SendTextMessage(fromUserName, toUserName, "没有搜索到相关内容");
                }
    
                StringBuilder sb = new StringBuilder();
    
                sb.Append("<xml>");
                sb.AppendFormat("<ToUserName><![CDATA[{0}]]></ToUserName>", fromUserName);
                sb.AppendFormat("<FromUserName><![CDATA[{0}]]></FromUserName>", toUserName);
                sb.AppendFormat("<CreateTime>{0}</CreateTime>", DateTime.Now);
                sb.Append("<MsgType><![CDATA[news]]></MsgType>");
                sb.AppendFormat("<ArticleCount>{0}</ArticleCount>", itemList.Count);
                sb.Append("<Articles>");
                foreach (ArticleItem item in itemList)
                {
                    sb.Append("<item>");
                    sb.AppendFormat("<Title><![CDATA[{0}]]></Title> ", item.title);
                    sb.AppendFormat("<Description><![CDATA[{0}]]></Description>", item.description);
                    sb.AppendFormat("<PicUrl><![CDATA[{0}]]></PicUrl>", item.picurl);
                    sb.AppendFormat("<Url><![CDATA[{0}]]></Url>", item.url);
                    sb.Append("</item>");
                }
                sb.Append("</Articles>");
                sb.Append("</xml>");
    
                return sb.ToString();
            }
    
            //http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token
            public static string GetAccessToken()
            {
                string accessToken = string.Empty;
                //http请求方式: GET
                //https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
                string query=string.Format("grant_type=client_credential&appid={0}&secret={1}",Common.AppID,Common.AppSecret);
                string result = WebApiRequest.GetRequest("https://api.weixin.qq.com", "/cgi-bin/token", query);
                //result返回说明
                //正常情况下,微信会返回下述JSON数据包给公众号:
                //{"access_token":"ACCESS_TOKEN","expires_in":7200}
                //参数    说明
                //access_token    获取到的凭证
                //expires_in    凭证有效时间,单位:秒
                //错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):
                //{"errcode":40013,"errmsg":"invalid appid"}
                JObject jOb = JObject.Parse(result);
                if (jOb["access_token"] != null)
                {
                    accessToken = jOb["access_token"].ToString(); ;
                }
                return accessToken;
            }
        }

     以上就搭建了一个简易的微信后台框架了。跟微信相关的内容大概也差不多了。想扩展内容、增加数据库支持等等,按照往常C#程序开发那样子做就好了。

  • 相关阅读:
    UVA12125 March of the Penguins (最大流+拆点)
    UVA 1317 Concert Hall Scheduling(最小费用最大流)
    UVA10249 The Grand Dinner(最大流)
    UVA1349 Optimal Bus Route Design(KM最佳完美匹配)
    UVA1212 Duopoly(最大流最小割)
    UVA1395 Slim Span(kruskal)
    UVA1045 The Great Wall Game(二分图最佳匹配)
    UVA12168 Cat vs. Dog( 二分图最大独立集)
    hdu3488Tour(KM最佳完美匹配)
    UVA1345 Jamie's Contact Groups(最大流+二分)
  • 原文地址:https://www.cnblogs.com/AlvinLiang/p/4171497.html
Copyright © 2011-2022 走看看