微信公众号开发笔记二(C#)

王君 原创 | 2015-01-16 17:18 | 收藏 | 投票
关键字:微信 公众号开发 

微信升级了公众号的接口,对用户和公众号的消息进行了双向加解密,为此原有的代码需要进行对应的加解密升级。根据微信提供的文档,只需要移植到自己的程序中,并且对关键信息进行修改即可,原有的业务逻辑不变。

完整代码如下:

<%@ WebHandler Language="C#" Class="WeChat" %>
 
using System;
using System.IO;
using System.Text;
using System.Web;
using System.Web.Security;
using System.Xml;
using System.Data;
using System.Data.SqlClient;
using System.Net;
 
public class WeChat : IHttpHandler
{    
    public void ProcessRequest(HttpContext context)
    {        
        weixin wx = new weixin();
        String postData = String.Empty;
 
        if (HttpContext.Current.Request.HttpMethod.ToUpper() == "POST")
        {
            LoadMenu();
            
            Stream s = HttpContext.Current.Request.InputStream;
            Byte[] b = new Byte[s.Length];
            s.Read(b, 0, (Int32)s.Length);
            postData = Encoding.UTF8.GetString(b);
 
            if (!String.IsNullOrEmpty(postData))
            {
                wx.Handle(postData);
            }
        }
        else
        {
            wx.Auth();
        }
    }
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
 
    /// <summary>
    /// 加载自定义菜单
    /// </summary>
    private void LoadMenu()
    {
        String menu = "";
        menu += "{\n";
        menu += "\"button\":[\n";
        menu += "{\n";
        menu += "\"name\":\"内容搜索\",\n";
        menu += "\"sub_button\":[\n";
        menu += "{\n";
        menu += "\"type\":\"click\",\n";
        menu += "\"name\":\"文章搜索\",\n";
        menu += "\"key\":\"SearchContent\"\n";
        menu += "},\n";
        menu += "{\n";
        menu += "\"type\":\"view\",\n";
        menu += "\"name\":\"杂志汇\",\n";
        menu += "\"url\":\"MediaIndex.aspx?FROM=WX\"\n";
        menu += "},\n";
        menu += "{\n";
        menu += "\"type\":\"view\",\n";
        menu += "\"name\":\"图书连载\",\n";
        menu += "\"url\":\"BookSerialise.aspx?FROM=WX\"\n";
        menu += "},\n";
        menu += "{\n";
        menu += "\"type\":\"view\",\n";
        menu += "\"name\":\"微媒体\",\n";
        menu += "\"url\":\"MiniBlogList.aspx?FROM=WX\"\n";
        menu += "}]\n";
        menu += "},\n";
        menu += "{\n";
        menu += "\"name\":\"社交搜索\",\n";
        menu += "\"sub_button\":[\n";
        menu += "{\n";
        menu += "\"type\":\"click\",\n";
        menu += "\"name\":\"姓名搜索\",\n";
        menu += "\"key\":\"SearchUser\"\n";
        menu += "},\n";
        menu += "{\n";
        menu += "\"type\":\"view\",\n";
        menu += "\"name\":\"人脉拓展\",\n";
        menu += "\"url\":\"PeopleList.aspx?FROM=WX\"\n";
        menu += "},\n";
        menu += "{\n";
        menu += "\"type\":\"view\",\n";
        menu += "\"name\":\"关系人\",\n";
        menu += "\"url\":\"ConnectionList.aspx?FROM=WX\"\n";
        menu += "}]\n";
        menu += "}]\n";
        menu += "}\n";
        
        String content = GetPage("https://api.weixin.qq.com/cgi-bin/menu/create?access_token=" + access_token, menu);
        
        HttpContext.Current.Response.Write(content);
    }
 
    /// <summary>
    /// 获取access_token
    /// </summary>
    private String GetAccessToken()
    {
        WebClient webClient = new WebClient();
        Byte[] bytes = webClient.DownloadData("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=【替换处】&secret=【替换处】");
        string result = Encoding.GetEncoding("utf-8").GetString(bytes);
        string[] temp = result.Split(',');
        string[] tp = temp[0].Split(':');
        return tp[1].ToString().Replace('"', ' ').Trim().ToString();
    }
    
    /// <summary>
    /// 发送POST请求创建自定义菜单
    /// </summary>
    public string GetPage(string posturl, string postData)
    {
        Stream outstream = null;
        Stream instream = null;
        StreamReader sr = null;
        HttpWebResponse response = null;
        HttpWebRequest request = null;
        Encoding encoding = Encoding.UTF8;
        byte[] data = encoding.GetBytes(postData);
        // 准备请求...
        try
        {
            // 设置参数
            request = WebRequest.Create(posturl) as HttpWebRequest;
            CookieContainer cookieContainer = new CookieContainer();
            request.CookieContainer = cookieContainer;
            request.AllowAutoRedirect = true;
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";
            request.ContentLength = data.Length;
            outstream = request.GetRequestStream();
            outstream.Write(data, 0, data.Length);
            outstream.Close();
            //发送请求并获取相应回应数据
            response = request.GetResponse() as HttpWebResponse;
            //直到request.GetResponse()程序才开始向目标网页发送Post请求
            instream = response.GetResponseStream();
            sr = new StreamReader(instream, encoding);
            //返回结果网页(html)代码
            string content = sr.ReadToEnd();
            string err = string.Empty;
            return content;
        }
        catch (Exception ex)
        {
            string err = ex.Message;
            HttpContext.Current.Response.Write(err);
            return string.Empty;
        }
    }
}
 
/// <summary>
/// 微信逻辑类
/// </summary>
public class weixin
{
    String Token = "【替换处】";
    String AppID = "【替换处】";
    String EncodingAESKey = "【替换处】";
    String DefaultResponseContent = "欢迎关注价值中国!价值中国是中国领先的财经商业领域社会化媒体。\r\n\r\n【系统关键字】\r\n『热点观察』:最具价值的新闻事件解读\r\n『新兴产业』:第一手的新兴产业评论\r\n『管理』:管理学的前沿理论\r\n『投资』:投资事件的专家观点\r\n『财经』:不同的视角解读经济事件\r\n『杂志汇』:财经类杂志的精品荟萃\r\n『新书连载』:精品新书的在线阅读\r\n\r\n【任意关键字】\r\n可搜索任意关键字,如:\r\n自贸区 互联网 货币 股票 大盘 基金 虚拟组织 儒学 佛学 艺术 自由 民主 美国经济 美元 利率 英国 欧洲 农民 土地 新能源 教育 领导力 创业 渠道 社会责任 营销 汽车 房地产 电信 高盛 英特尔 腾讯......等等,您能想到的任何主题,我们都有!";
    String SearchContent = "【文章搜索】在价值中国微信公众号的“对话框”中,输入任意“关键字”,即可搜索数百万篇精彩专家文章!";
    String SearchUser = "【姓名搜索】在价值中国微信公众号的“对话框”中,输入任意“姓名”,数百万职业人士期待与您社交!";
 
    /// <summary>
    /// 开发者身份验证
    /// </summary>
    public void Auth()
    {
        String echoStr = HttpContext.Current.Request.QueryString["echoStr"];
 
        if (CheckSignature())
        {
            if (!String.IsNullOrEmpty(echoStr))
            {
                HttpContext.Current.Response.Write(echoStr);
                HttpContext.Current.Response.End();
            }
        }
    }
 
    /// <summary>
    /// 签名验证
    /// </summary>
    private bool CheckSignature()
    {
        String signature = HttpContext.Current.Request.QueryString["signature"];
        String timestamp = HttpContext.Current.Request.QueryString["timestamp"];
        String nonce = HttpContext.Current.Request.QueryString["nonce"];
        String[] ArrTmp = { Token, timestamp, nonce };
 
        Array.Sort(ArrTmp);
        String tmpStr = String.Join("", ArrTmp);
 
        tmpStr = FormsAuthentication.HashPasswordForStoringInConfigFile(tmpStr, "SHA1");
        tmpStr = tmpStr.ToLower();
 
        if (tmpStr == signature)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    
    /// <summary>
    /// 处理用户发来的消息
    /// </summary>
    public void Handle(String postData)
    {
        Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(Token, EncodingAESKey, AppID);
 
        string msg_signature = HttpContext.Current.Request.QueryString["msg_signature"];
        String timestamp = HttpContext.Current.Request.QueryString["timestamp"];
        String nonce = HttpContext.Current.Request.QueryString["nonce"];
        
        string userMsg = "";
        int ret = wxcpt.DecryptMsg(msg_signature, timestamp, nonce, postData, ref userMsg);
 
        if (ret != 0)
        {
            Logger.Log(Logger.LogName.ByDate, "签名验证错误: ", ret.ToString());
            return;
        }
 
        //Logger.Log(Logger.LogName.ByDate, "解密后的数据", userMsg);
                
        XmlDocument doc = new XmlDocument();
        doc.LoadXml(userMsg);
 
        XmlElement rootElement = doc.DocumentElement;
        XmlNode MsgType = rootElement.SelectSingleNode("MsgType");
 
        RequestXML requestXML = new RequestXML();
        requestXML.ToUserName = rootElement.SelectSingleNode("ToUserName").InnerText;
        requestXML.FromUserName = rootElement.SelectSingleNode("FromUserName").InnerText;
        requestXML.CreateTime = rootElement.SelectSingleNode("CreateTime").InnerText;
        requestXML.MsgType = MsgType.InnerText;
 
        if (requestXML.MsgType == "event")
        {
            requestXML.Event = rootElement.SelectSingleNode("Event").InnerText;
            requestXML.EventKey = rootElement.SelectSingleNode("EventKey").InnerText;
        }
        
        if (requestXML.MsgType == "text")
        {
            requestXML.Content = rootElement.SelectSingleNode("Content").InnerText;
        }
        
        ResponseMsg(requestXML);
    }
 
    /// <summary>
    /// 处理回复给用户的消息
    /// </summary>
    /// <param name="weixinXML"></param>
    private void ResponseMsg(RequestXML requestXML)
    {
        String responseContent = String.Empty;
 
        if (requestXML.MsgType == "event")
        {
            switch (requestXML.Event)
            {
                case "subscribe":
                    responseContent = FormatTextXML(requestXML.FromUserName, requestXML.ToUserName, DefaultResponseContent);
                    break;
 
                case "CLICK":
                    if (requestXML.EventKey == "SearchContent")
                    {
                        responseContent = FormatTextXML(requestXML.FromUserName, requestXML.ToUserName, SearchContent);
                    }
                    
                    if (requestXML.EventKey == "SearchUser")
                    {
                        responseContent = FormatTextXML(requestXML.FromUserName, requestXML.ToUserName, SearchUser);
                    }
                    break;
            }
        }
        
        if (requestXML.MsgType == "text")
        {
            String systemMsg = SystemKey(requestXML.Content);
 
            if (!String.IsNullOrEmpty(systemMsg))
            {
                responseContent = FormatTextXML(requestXML.FromUserName, requestXML.ToUserName, systemMsg);
            }
            else
            {
                //业务逻辑
 
                responseContent = FormatTextXML(requestXML.FromUserName, requestXML.ToUserName, 【处理业务逻辑后的结果】);
            }
        }
                
        HttpContext.Current.Response.ContentEncoding = Encoding.UTF8;
        HttpContext.Current.Response.Clear();
        HttpContext.Current.Response.Write(responseContent);
    }
 
    /// <summary>
    /// 格式化要输出给用户的XML内容
    /// </summary>
    private String FormatTextXML(String fromUserName, String toUserName, String content)
    {
        Tencent.WXBizMsgCrypt wxcpt = new Tencent.WXBizMsgCrypt(Token, EncodingAESKey, AppID);
        String tempTimestamp = ConvertDateTimeInt(DateTime.Now).ToString();
        String tempNonce = HttpContext.Current.Request.QueryString["nonce"];
        
        //明文回复
        String replyContent = "<xml><ToUserName><![CDATA[" + fromUserName + "]]></ToUserName><FromUserName><![CDATA[" + toUserName + "]]></FromUserName><CreateTime>" + tempTimestamp + "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" + content + "]]></Content></xml>";
        //Logger.Log(Logger.LogName.ByDate, "明文回复", replyContent);
        
        //加密逻辑
        string sEncryptMsg = "";
        wxcpt.EncryptMsg(replyContent, tempTimestamp, tempNonce, ref sEncryptMsg);
 
        return sEncryptMsg;
    }
 
    /// <summary>
    /// datetime转换为unixtime
    /// </summary>
    /// <param name="time"></param>
    /// <returns></returns>
    private int ConvertDateTimeInt(System.DateTime time)
    {
        System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
        return (int)(time - startTime).TotalSeconds;
    }
}
 
//微信请求类
public class RequestXML
{
    private String toUserName = String.Empty;
 
    /// <summary>
    /// 消息接收方微信号,一般为公众平台账号微信号
    /// </summary>
    public String ToUserName
    {
        get { return toUserName; }
        set { toUserName = value; }
    }
 
    private String fromUserName = "";
 
    /// <summary>
    /// 消息发送方微信号
    /// </summary>
    public String FromUserName
    {
        get { return fromUserName; }
        set { fromUserName = value; }
    }
 
    private String createTime = String.Empty;
 
    /// <summary>
    /// 创建时间
    /// </summary>
    public String CreateTime
    {
        get { return createTime; }
        set { createTime = value; }
    }
 
    private String msgType = String.Empty;
 
    /// <summary>
    /// 信息类型 地理位置:location,文本消息:text,消息类型:image
    /// </summary>
    public String MsgType
    {
        get { return msgType; }
        set { msgType = value; }
    }
 
    private String m_Event = String.Empty;
 
    /// <summary>
    /// 信息类型关键字event内容 添加:subscribe 取消:unsubscribe
    /// </summary>
    public String Event
    {
        get { return m_Event; }
        set { m_Event = value; }
    }
 
    private String eventKey = String.Empty;
 
    /// <summary>
    /// EventKey
    /// </summary>
    public String EventKey
    {
        get { return eventKey; }
        set { eventKey = value; }
    }
 
    private String content = String.Empty;
 
    /// <summary>
    /// 信息内容
    /// </summary>
    public String Content
    {
        get { return content; }
        set { content = value; }
    }
 
    private String location_X = String.Empty;
 
    /// <summary>
    /// 地理位置纬度
    /// </summary>
    public String Location_X
    {
        get { return location_X; }
        set { location_X = value; }
    }
 
    private String location_Y = String.Empty;
 
    /// <summary>
    /// 地理位置经度
    /// </summary>
    public String Location_Y
    {
        get { return location_Y; }
        set { location_Y = value; }
    }
 
    private String scale = String.Empty;
 
    /// <summary>
    /// 地图缩放大小
    /// </summary>
    public String Scale
    {
        get { return scale; }
        set { scale = value; }
    }
 
    private String mapInfo = String.Empty;
 
    /// <summary>
    /// 地理位置信息
    /// </summary>
    public String MapInfo
    {
        get { return mapInfo; }
        set { mapInfo = value; }
    }
 
    private String picUrl = String.Empty;
 
    /// <summary>
    /// 图片链接,开发者可以用HTTP GET获取
    /// </summary>
    public String PicUrl
    {
        get { return picUrl; }
        set { picUrl = value; }
    }
}
 
 
手机版价值中国访问:m.chinavalue.net
 
 
扫描二维码,添加价值中国微信公众号。
 

王君 的近期作品

个人简介
负责价值中国,价值家的产品开发、系统维护、日常运营等。
每日关注 更多
王君 的日志归档
[查看更多]
赞助商广告