如何使用C#建立响应微信接入和自动回复的代码

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何使用C#建立响应微信接入和自动回复的代码相关的知识,希望对你有一定的参考价值。

打开Visual Studio 2010, 新建一个ASPX WEB应用程序类型的项目, Visual Studio 会帮我们自动地创建一些得得啵啵的文件,直接不理会,创建完毕后,如下图右击项目,选择“添加”-“新建项”,如下图选择“一般处理程序”,并且命名为"mp.ashx"。

创建好了,添加如下的引用:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.XPath;using System.Security;
using System.Net;
using System.Collections;
using System.Security.Cryptography;
提示:一般处理程序,可以返回各种格式的HTTP响应数据,甚至包括但不限于GIF等图像的格式哦,非常地强大而且灵活,但开发难度有点高,有一点类似LOL 中的盲僧,因微信公众平台支持探返回的数据格式一般是XML,但也包括一些非XML的字串什么的,所以我们使用一般处理程序可以非常好完成任务。

2 编写signature校验函数

接口的接入过程其实很简单,用糙人的一句话就是,哥我喊一个“哎。。。”,妹纸你回一个“哎。。。”,咱就结婚成功了(程序直接回复echostr参数),当然要更深度的结合,决不止那么简单。
哥哥您想呀,如果夜半三更,爬上妹妹床头的不是哥哥您,而是一个叫dosboy的骇客,后果我都没有办法再想下去了,所以微信开通前,妹纸您要告诉哥哥(微信平台)口令(在微信中预设一个令牌Token)。
哥哥每回有接入要求的时候,妹纸验明正身就可以拉,因为网络都是明文(dosboy会在窗下偷听的哦),所以这个验证不能直接传递Token,而要通过一定算法,微信的文档下:
根据微信的文档,开发者提交信息后,微信服务器将发送GET请求到填写的URL上,GET请求携带四个参数:

参数
描述

signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp 时间戳
nonce 随机数
echostr 随机字符串

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。

加密/校验流程如下: 1. 将token、timestamp、nonce三个参数进行字典序排序 2. 将三个参数字符串拼接成一个字符串进行sha1加密 3. 开发者获得加密后的字符串可与signature对比,
根据以上信息,我写了一个函数,这个函数一定要使用,如果您坚持不验明正身,平台代码也可以接入的,但是危险程度您自己判断:

private bool checkSignature(string signature,string timestamp,string nonce,string token)

ArrayList tmpArray = new ArrayList();
tmpArray.Add(token);
tmpArray.Add(timestamp);
tmpArray.Add(nonce);
tmpArray.Sort();
string tmpStr = (string)tmpArray[0] + (string)tmpArray[1] + (string)tmpArray[2];

//建立SHA1对象
SHA1 sha = new SHA1CryptoServiceProvider();

//将mystr转换成byte[]
ASCIIEncoding enc = new ASCIIEncoding();
byte[] dataToHash = enc.GetBytes(tmpStr);

//Hash运算
byte[] dataHashed = sha.ComputeHash(dataToHash);

//将运算结果转换成string
string hash = BitConverter.ToString(dataHashed).Replace("-", "");
log("hash:" + hash); //记录日志,不需要可以注释掉

if(hash.ToLower()==signature.ToLower())
return true;
else
return false;



程序中我用了一个记录日志的函数,在这里我也贴上来,如果大家不需可以可以注释掉红色的那行:
private void log(string stringinput)

StreamWriter sw = new StreamWriter("C:\\微信日志.txt", true);
sw.WriteLine(DateTime.Now.ToString() + "," + stringinput);
sw.Close();


3 接入接口写入数据库代码
想搞微信平台就要接入几百个或更多公众号,必竟委托给你的妹纸起码三宫六院,做好管理还是有必要的,数据库的技术是必须滴,所以在这里,我使用了SQL SERVER 2008R2,把所有的接入信息都写入数据库的“微信号列表”,这里我就简简单单使用了数据库的Update,函数为 LandMPUpdateSQLServer() 代码如下:
表的结构我就截个图,如果看官只想测试一下如何接入,直接闪现到下一节。

private void LandMPUpdateSQLServer(HttpContext context)

SqlConnection connMpDb = new SqlConnection(connectionString); //connectionString为预先定义的连接串
connMpDb.Open();
SqlCommand comMpDb = new SqlCommand("update 微信号列表 set signature=@signature,timestamp=@timestamp,nonce=@nonce,echostr=@echostr,[验证]=1 Where ID=*****", connMpDb);
foreach (string s in OpenParameters) //OpenParameters 为4个参数名的数组,即 "signature","timestamp","nonce","echostr"

SqlParameter sp = comMpDb.Parameters.Add("@" + s, System.Data.SqlDbType.VarChar);
if (context.Request.QueryString.AllKeys.Contains(s))
sp.Value = context.Request.QueryString[s];
else
sp.Value = "No Value";


comMpDb.ExecuteNonQuery();
connMpDb.Close();


4 完成代码编写接入接口

(1)请加上变量声明:
在如下的位置后面,即mp类里填加上2个类的变量的定义
public class mp : IHttpHandler


//接入参数
private string[] OpenParameters = "signature", "timestamp", "nonce", "echostr" ;

//SQL连接字串,您可以定义成一个字符串,我是从系统设置里取的
private string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["testmpConnectionString"].ConnectionString;

(2)请完成主程序:
请在

public void ProcessRequest(HttpContext context) 内填加上如下的语句
提示: 这是一个系统函数,每次微信有事件发生时(比如有人给你发消息拉,加你为关注拉什么的)的微信就会主动出动联系你给它填的URL地址,这个函数内的代码就会激发运行!
【自动回复】 如下的代码运行,不仅仅会让您的程序接入微信的公众平台,并且还能自动重复回复别人发的话,看看如下的效果图,你和你的小伙伴是不是已经惊呆了!如果您不要这个功能的话这个程序,您只要保留
1-16行的代码,其它全删除!就已经完全做到接入微信公众平台,如果您再想您的程序智能一点,比如您的妹纸给你发,“我现在就要”,你自动回复 “本人正在忙”(活该你单身),请改造17行以后的代码,完全够用了。
【数据库】 不想碰数据库,就注释掉14行

1 bool isLanding = true;
2 foreach (string s in OpenParameters)
3
4 if (!context.Request.QueryString.AllKeys.Contains(s))
5 isLanding = false;
6
7
8
9 //toke 为 AAA时验证是否是微信的请求
10 if (isLanding && checkSignature(context.Request.QueryString["signature"], context.Request.QueryString["timestamp"], context.Request.QueryString["nonce"],"AAA"))
11
12 context.Response.ContentType = "text/plain";
13 string echoString = context.Request.QueryString["echostr"];
14 LandMPUpdateSQLServer(context);
15 context.Response.Write(echoString);
16
17 else
18
19 //读取发过来的信息到inputXml变量中
20 Stream sin = context.Request.InputStream;
21 byte[] readBytes;
22 readBytes = new byte[sin.Length];
23 sin.Read(readBytes, 0, readBytes.Length);
24 string inputXml = Encoding.UTF8.GetString(readBytes);
25
26 //使用XMLDocument加载信息结构
27 XmlDocument xmlDoc = new XmlDocument();
28 xmlDoc.LoadXml(inputXml);
29 string stringMsgId = xmlDoc.SelectSingleNode("//MsgId").InnerText;
30
31 //把传过来的XML数据各个字段区分出来,并且填到fields这个字典变量中去
32 Dictionary<string, string> fields = new Dictionary<string, string>();
33 foreach (XmlNode x in xmlDoc.SelectSingleNode("/xml").ChildNodes)
34
35 fields.Add(x.Name, x.InnerText);
36
37
38 //形成返回格式的XML文档
39 string returnXml = "<xml><ToUserName><![CDATA[" +
40 fields["FromUserName"] + "]]></ToUserName><FromUserName><![CDATA[" +
41 fields["ToUserName"] + "]]></FromUserName><CreateTime>" +
42 DateTime.Now.Subtract(new DateTime(1970, 1, 1, 8, 0, 0)).TotalSeconds.ToString() + "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" +
43 fields["Content"] + "]]></Content></xml>";
44
45 context.Response.ContentType = "text/xml";
46 context.Response.Write(returnXml);
47
48

5 把程序发布到互联网上(此处不解释),注意一定要80端口

(此处省略18000字,记得记上稿费)

6 在微信公众平台上,做如下设置:

在公众平台网站的高级功能 – 开发模式页,点击“成为开发者”按钮,填写URL和Token,其中URL是开发者用来接收微信服务器数据的接口URL。Token可由开发者任意填写, 用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。
参考技术A 可参考如下的部分:
http://www.cnblogs.com/dosboy/p/3904453.html

微信平台ASPX高级定制开发(一):如何使用C#建立响应微信接入和自动回复的代码
大神如此多,为何空寂寞。

C#微信公众号开发 -- 用户关注之后自动回复

通过了上一篇文章之后的微信开发者验证之后,我们就可以做微信公众号的代码开发了。

当我们点击关注某个公众号的时候,有时候会发现他会自动给我们回复一条消息,比如欢迎关注XXX公众号。这个功能其实是在点击关注的时候,用户触发了微信定义的事件,同时微信会返回给我们一个XML数据包,微信官方的解释如下:

推送XML数据包示例:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[FromUser]]></FromUserName>
<CreateTime>123456789</CreateTime>
<MsgType><![CDATA[event]]></MsgType>
<Event><![CDATA[subscribe]]></Event>
</xml>

参数说明:

参数描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,event
Event 事件类型,subscribe(订阅)、unsubscribe(取消订阅)

 从示例中可以看出,如果我们想要处理用户点击的关注事件,那么必须要知道消息类型MsgType,事件类型Event。所以我们可以仿照着微信给我们的XML示例用C#建立一下事件接受的类,具体代码如下:

public class wxmessage
    {
        /// <summary>
        /// 本公众帐号
        /// </summary>
        public string ToUserName { get; set; }
        /// <summary>
        /// 用户帐号
        /// </summary>
        public string FromUserName { get; set; }
        /// <summary>
        /// 发送时间戳
        /// </summary>
        public string CreateTime { get; set; }
        /// <summary>
        /// 发送的文本内容 
        /// </summary>
        public string Content { get; set; }
        /// <summary>
        /// 消息的类型
        /// </summary>
        public string MsgType { get; set; }
        /// <summary>
        /// 事件名称
        /// </summary>
        public string EventName { get; set; }
        
        //这两个属性会在后面的讲解中提到
        public string Recognition { get; set; }
        public string EventKey { get; set; } 
    }

事件类建立完成之后,我们就可以在wxapi.aspx页面中做事件处理的逻辑操作了,请看下面的代码:

public partial class wxapi : System.Web.UI.Page
    {
        const string _token = "在微信公众测试号后台写的那个Token";
        private const string _myOpenid = "你自己微信公众测试号的appID";
        string postStr = "";
        protected void Page_Load(object sender, EventArgs e)
        {

            //************** 验证成为开发者的时候将此代码注释 ***********//
            //对微信的信息进行处理和应用
            WXOpera();

            //***********  验证成为开发者之后将此代码注释 *************//
            //string httpMethod = Request.HttpMethod.ToLower();
            //if (httpMethod == "post")
            //{
            //    //第一次验证的时候开启
            //    FirstValid();
            //}
            //else
            //{
            //    Valid();  //如果不是post请求就去做开发者验证
            //}
        }

        /// <summary>
        /// 验证成为开发者
        /// </summary>
        private void Valid()
        {
            string echoStr = Request.QueryString["echoStr"].ToString();
            if (CheckSignature())
            {
                if (!string.IsNullOrEmpty(echoStr))
                {
                    Response.Write(echoStr);
                    Response.End();
                }
            }
        }

        /// <summary>
        /// 验证微信签名
        /// </summary>
        /// * 将token、timestamp、nonce三个参数进行字典序排序
        /// * 将三个参数字符串拼接成一个字符串进行sha1加密
        /// * 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信。
        /// <returns></returns>
        private bool CheckSignature()
        {
            string signature = Request.QueryString["signature"].ToString();
            string timestamp = Request.QueryString["timestamp"].ToString();
            string nonce = Request.QueryString["nonce"].ToString();
            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>
        private void FirstValid()
        {
            Stream s = System.Web.HttpContext.Current.Request.InputStream;
            byte[] b = new byte[s.Length];
            s.Read(b, 0, (int)s.Length);
            postStr = Encoding.UTF8.GetString(b);
            if (!string.IsNullOrEmpty(postStr))
            {
                ResponseMsg(postStr);
            }
        }

        /// <summary>
        /// 返回信息结果(微信信息返回)
        /// </summary>
        /// <param name="weixinXML"></param>
        private void ResponseMsg(string weixinXML)
        {
            //回复消息的部分:你的代码写在这里
        }

        /// <summary>
        /// 微信操作
        /// </summary>
        private void WXOpera()
        {
            wxmessage wx = GetWxMessage();
            string res = "";
            if (!string.IsNullOrEmpty(wx.EventName) && wx.EventName.Trim() == "subscribe")
            {
                //刚关注时的时间,用于欢迎词
                string content = "";
                content = "您好,欢迎关注XXX公众号";                
res
= sendTextMessage(wx, content); HttpContext.Current.Response.Write(res); HttpContext.Current.Response.End(); } } /// <summary> /// 获取和设置微信类中的信息 /// </summary> /// <returns></returns> private wxmessage GetWxMessage() { wxmessage wx = new wxmessage(); StreamReader str = new StreamReader(Request.InputStream, Encoding.UTF8); XmlDocument xml = new XmlDocument(); xml.Load(str); str.Close(); str.Dispose(); 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> /// <param name="wx" />获取的收发者信息 /// <param name="content" />内容 /// <returns></returns> private string sendTextMessage(wxmessage wx, string content) { string res = string.Format(Message_Text, wx.FromUserName, wx.ToUserName, DateTime.Now.Ticks, content); return res; } /// <summary> /// 普通文本消息 /// </summary> private static string Message_Text { get { return @"<xml> <ToUserName><![CDATA[{0}]]></ToUserName> <FromUserName><![CDATA[{1}]]></FromUserName> <CreateTime>{2}</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[{3}]]></Content> </xml>"; } } }

其中:Message_Text属性,是微信定义的发送文本消息格式,下面是微信官方给出的解释:

文本消息格式:

<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>

文本消息格式说明:
参数是否必须描述
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间 (整型)
MsgType text
Content 回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示)

注:有的朋友可能会将这里的ToUserName和FromUserName,与接收事件的ToUserName和FromUserName混淆,可能你看出来了sendTextMessage()方法中参数顺序与Message_Text属性中的顺序不一致。

然后重新将wxapi.aspx页面发布到服务器上面,随后用自己的微信号关注测试公众号(扫描测试公众号的二维码),关注完成之后,如果你能看到:您好,欢迎关注XXX公众号 ,则说明自动回复设置成功!

 

以上是关于如何使用C#建立响应微信接入和自动回复的代码的主要内容,如果未能解决你的问题,请参考以下文章

分享为小程序添加自动回复消息的5种方法!自动客服功能的微信小程序

C#微信公众号开发 -- 用户关注之后自动回复

JAVA微信公众号开发回复消息能回复多条吗?具体怎么代码实现?

使用微信公众平台自动回复 API 时候,如何向服务器提交 xml 消息内容?

使用微信小程序客服消息上的一些注意事项!小程序客服消息按钮,接入及消息接收

Unity:如何使用 C# 使 Canvas UI 响应所有屏幕