• 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#程序开发那样子做就好了。

  • 相关阅读:
    DNS解析的并发性
    Pycharm(Jetbrains IDE)Debian buster Navigate Back/Forward (Ctrl+Alt+Left/Right)不好使的解决方法
    Linux命令行登录时的提示信息
    cmake编译Qt5
    cmake使用ccache
    bash 脚本所在文件夹
    gnome desktop
    gnome caps lock 和 num lock 键状态
    oracle 日期、月份处理
    独夜行
  • 原文地址:https://www.cnblogs.com/AlvinLiang/p/4171497.html
Copyright © 2020-2023  润新知