【引言】

   利用asp.net搭建微信公众平台的案例并不多,微信官方给的案例是用PHP的,网上能找到的代码很多也是存在着这样那样的问题或者缺少部分方法,无法使用,下面是我依照官方文档写的基于.net 搭建微信公众平台源代码。由于经验不足,内可能存在不严谨之处,欢迎交流。

【分析】

   实现的功能较为简单,主要分为验证与消息接收回复两部分,首先是验证:

  

  这已经是验证好后的截图了,需要输入的是URL和你自己设定的Token码,URL为你上传服务器的地址例如:http://XXXXX.com/weixin/weixin.aspx,这里着重要强调的一个问题是加不加WWW的差异,无论加不加WWW,在验证时都是没有影响的,但在接收消息时,加和不加的差异是我最初无法接收到用户消息的直接原因,原因可能和POST带XML的请求机制有关。落实到具体项目中加还是不加,和你配置的DNS等有关,要是不想深入研究,当接收不到用户消息,而公众平台的调试工具里又是正常的,那你可以交替试试。Token码可以随机指定,但必须和代码中的Token指定相同,如果系统安全性要求比较高,建议增加token的复杂程度。

  在下面代码第一行中指定token,必须与上面一直

      const string Token = "nidaye1234";//你的token
#region 以下代码只用于第一次验证 验证完后请注释
protected void Page_Load(object sender, EventArgs e)
{
string postStr = "";
if (Request.HttpMethod.ToLower() == "post")
{
System.IO.Stream s = System.Web.HttpContext.Current.Request.InputStream;
byte[] b = new byte[s.Length];
s.Read(b, 0, (int)s.Length);
postStr = System.Text.Encoding.UTF8.GetString(b);
if (!string.IsNullOrEmpty(postStr))
{ Response.End();
}
//WriteLog("postStr:" + postStr);
}
else
{
Valid();
}
}
#endregion

上面的代码只是pageLoad,里面还要调用到的验证方法这里先不写,在最后会给出全部的源代码。

  验证成功后可以调用api实现消息收发,下面是微信官方给的文档

  接收消息

  

  发送消息

  以及关注事件

  我的做法是首先创建一个接收消息实体以及实体填充方法,如下,其中根据MsgType的不同,选择填充合适的Content或者EventName。当然我这里没有使用微信为开发者提供的高级功能(例如语音定位之类的),如有用到可以增加这个类的属性,并对应修改填充器即可。

         private class ExmlMsg
{
/// <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; } } private ExmlMsg GetExmlMsg(XmlElement root)
{
ExmlMsg xmlMsg = new ExmlMsg() {
FromUserName = root.SelectSingleNode("FromUserName").InnerText,
ToUserName = root.SelectSingleNode("ToUserName").InnerText,
CreateTime = root.SelectSingleNode("CreateTime").InnerText,
MsgType = root.SelectSingleNode("MsgType").InnerText,
};
if (xmlMsg.MsgType.Trim().ToLower() == "text")
{
xmlMsg.Content = root.SelectSingleNode("Content").InnerText;
}
else if (xmlMsg.MsgType.Trim().ToLower() == "event")
{
xmlMsg.EventName = root.SelectSingleNode("Event").InnerText;
}
return xmlMsg;
}

  普通使用时(非验证时)的pageload,这个方法主要通过调用PostInput()(详间最后的源代码)还获取post过来的数据,并将它们传入消息适配器中。

         /// <summary>
/// 以下是正常使用时的pageload 请在验证时将其注释 并保证在正常使用时可用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{ if (Request.HttpMethod == "POST")
{
string weixin = "";
weixin = PostInput();//获取xml数据
if (!string.IsNullOrEmpty(weixin))
{
ResponseMsg(weixin);////调用消息适配器
}
}
}

  以下消息适配器,通过MsgType来区分消息的类型,并调用对应的方法,这里偷了一个懒,就是用户首次关注时推送消息的方法没有抽象出去,因为我暂时也没有别的enven可调用,如果结构复杂时,可以自行抽象。如果需要改变欢迎词的内容改变msg的值即可。日后扩展新功能时,可以根据case的的MsgType新写对应的方法,我这里主要用到的是textCase(),需要传入用户发送过来的消息实体,因为你的业务逻辑中可能需要用到发件者的各种信息。

        private void ResponseMsg(string weixin)// 服务器响应微信请求
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(weixin);//读取xml字符串
XmlElement root = doc.DocumentElement;
ExmlMsg xmlMsg = GetExmlMsg(root);
//XmlNode MsgType = root.SelectSingleNode("MsgType");
//string messageType = MsgType.InnerText;
string messageType = xmlMsg.MsgType;//获取收到的消息类型。文本(text),图片(image),语音等。 try
{ switch (messageType)
{
//当消息为文本时
case "text":
textCase(xmlMsg);
break;
case "event":
if (!string.IsNullOrEmpty(xmlMsg.EventName) && xmlMsg.EventName.Trim() == "subscribe")
{
//刚关注时的时间,用于欢迎词
int nowtime = ConvertDateTimeInt(DateTime.Now);
string msg = "你要关注我,我有什么办法。随便发点什么试试吧~~~";
string resxml = "<xml><ToUserName><![CDATA[" + xmlMsg.FromUserName + "]]></ToUserName><FromUserName><![CDATA[" + xmlMsg.ToUserName + "]]></FromUserName><CreateTime>" + nowtime + "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" + msg + "]]></Content><FuncFlag>0</FuncFlag></xml>";
Response.Write(resxml);
}
break;
case "image":
break;
case "voice":
break;
case "vedio":
break;
case "location":
break;
case "link":
break;
default:
break;
}
Response.End();
}
catch (Exception)
{ }
}

  获取文本回复信息方法,主要目的是按照官方文档的要求,拼接出所要返回给微信服务器的xml格式。它的msg内容来自于getText方法,同样需要传入用户消息实体,我下面给出了我用来测试的方法的内容。

         private void textCase(ExmlMsg xmlMsg)
{
int nowtime = ConvertDateTimeInt(DateTime.Now);
string msg = "";
msg = getText(xmlMsg);
string resxml = "<xml><ToUserName><![CDATA[" + xmlMsg.FromUserName + "]]></ToUserName><FromUserName><![CDATA[" + xmlMsg.ToUserName + "]]></FromUserName><CreateTime>" + nowtime + "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" + msg + "]]></Content><FuncFlag>0</FuncFlag></xml>";
Response.Write(resxml); }
         private string getText(ExmlMsg xmlMsg)
{
string con = xmlMsg.Content.Trim(); System.Text.StringBuilder retsb = new StringBuilder();
retsb.Append("这是测试返回");
retsb.Append("接收到的消息:"+ xmlMsg.Content);
retsb.Append("用户的OPEANID:"+ xmlMsg.FromUserName); return retsb.ToString();
}

  如上代码,我得到的测试结果

    好了,可以收工了,这里还会用到的是时间转换的方法,因为文档规定的时间戳为int类型。于是网上找了转换方法。详见下面的完整代码。

【完整代码】

      太长了,我折起来了,需要打开,我开始用的是MVC,但后来想想没必要,就用了普通的aspx,其实一般处理程序(ashx)也行,而且性能会更好点。我可以保证以下代码完整,不会缺方法,被坑怕了,呵呵。

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Web.Security;
using System.Text.RegularExpressions; namespace WhoLove.weixin
{
public partial class weixin : System.Web.UI.Page
{ const string Token = "nidaye1234";//你的token
#region 以下代码只用于第一次验证 验证完后请注释
//protected void Page_Load(object sender, EventArgs e)
//{
// string postStr = "";
// if (Request.HttpMethod.ToLower() == "post")
// {
// System.IO.Stream s = System.Web.HttpContext.Current.Request.InputStream;
// byte[] b = new byte[s.Length];
// s.Read(b, 0, (int)s.Length);
// postStr = System.Text.Encoding.UTF8.GetString(b);
// if (!string.IsNullOrEmpty(postStr))
// {
// Response.End();
// }
// //WriteLog("postStr:" + postStr);
// }
// else
// {
// Valid();
// }
//}
#endregion #region 以下是正常使用时的pageload 请在验证时将其注释 并保证在正常使用时可用
/// <summary>
/// 以下是正常使用时的pageload 请在验证时将其注释 并保证在正常使用时可用
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void Page_Load(object sender, EventArgs e)
{ if (Request.HttpMethod == "POST")
{
string weixin = "";
weixin = PostInput();//获取xml数据
if (!string.IsNullOrEmpty(weixin))
{
ResponseMsg(weixin);//调用消息适配器
}
}
}
#endregion #region 获取post请求数据
/// <summary>
/// 获取post请求数据
/// </summary>
/// <returns></returns>
private string PostInput()
{
Stream s = System.Web.HttpContext.Current.Request.InputStream;
byte[] b = new byte[s.Length];
s.Read(b, 0, (int)s.Length);
return Encoding.UTF8.GetString(b);
}
#endregion #region 消息类型适配器
private void ResponseMsg(string weixin)// 服务器响应微信请求
{
XmlDocument doc = new XmlDocument();
doc.LoadXml(weixin);//读取xml字符串
XmlElement root = doc.DocumentElement;
ExmlMsg xmlMsg = GetExmlMsg(root);
//XmlNode MsgType = root.SelectSingleNode("MsgType");
//string messageType = MsgType.InnerText;
string messageType = xmlMsg.MsgType;//获取收到的消息类型。文本(text),图片(image),语音等。 try
{ switch (messageType)
{
//当消息为文本时
case "text":
textCase(xmlMsg);
break;
case "event":
if (!string.IsNullOrEmpty(xmlMsg.EventName) && xmlMsg.EventName.Trim() == "subscribe")
{
//刚关注时的时间,用于欢迎词
int nowtime = ConvertDateTimeInt(DateTime.Now);
string msg = "你要关注我,我有什么办法。随便发点什么试试吧~~~";
string resxml = "<xml><ToUserName><![CDATA[" + xmlMsg.FromUserName + "]]></ToUserName><FromUserName><![CDATA[" + xmlMsg.ToUserName + "]]></FromUserName><CreateTime>" + nowtime + "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" + msg + "]]></Content><FuncFlag>0</FuncFlag></xml>";
Response.Write(resxml);
}
break;
case "image":
break;
case "voice":
break;
case "vedio":
break;
case "location":
break;
case "link":
break;
default:
break;
}
Response.End();
}
catch (Exception)
{ }
}
#endregion private string getText(ExmlMsg xmlMsg)
{
string con = xmlMsg.Content.Trim(); System.Text.StringBuilder retsb = new StringBuilder(200);
retsb.Append("这里放你的业务逻辑");
retsb.Append("接收到的消息:"+ xmlMsg.Content);
retsb.Append("用户的OPEANID:"+ xmlMsg.FromUserName); return retsb.ToString();
} #region 操作文本消息 + void textCase(XmlElement root)
private void textCase(ExmlMsg xmlMsg)
{
int nowtime = ConvertDateTimeInt(DateTime.Now);
string msg = "";
msg = getText(xmlMsg);
string resxml = "<xml><ToUserName><![CDATA[" + xmlMsg.FromUserName + "]]></ToUserName><FromUserName><![CDATA[" + xmlMsg.ToUserName + "]]></FromUserName><CreateTime>" + nowtime + "</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[" + msg + "]]></Content><FuncFlag>0</FuncFlag></xml>";
Response.Write(resxml); }
#endregion #region 将datetime.now 转换为 int类型的秒
/// <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;
}
private int converDateTimeInt(System.DateTime time)
{
System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
return (int)(time - startTime).TotalSeconds;
} /// <summary>
/// unix时间转换为datetime
/// </summary>
/// <param name="timeStamp"></param>
/// <returns></returns>
private DateTime UnixTimeToTime(string timeStamp)
{
DateTime dtStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));
long lTime = long.Parse(timeStamp + "0000000");
TimeSpan toNow = new TimeSpan(lTime);
return dtStart.Add(toNow);
}
#endregion #region 验证微信签名 保持默认即可
/// <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;
}
} private void Valid()
{
string echoStr = Request.QueryString["echoStr"].ToString();
if (CheckSignature())
{
if (!string.IsNullOrEmpty(echoStr))
{
Response.Write(echoStr);
Response.End();
}
}
}
#endregion #region 写日志(用于跟踪) + WriteLog(string strMemo, string path = "*****")
/// <summary>
/// 写日志(用于跟踪)
/// 如果log的路径修改,更改path的默认值
/// </summary>
private void WriteLog(string strMemo, string path = "wx.txt")
{
string filename = Server.MapPath(path);
StreamWriter sr = null;
try
{
if (!File.Exists(filename))
{
sr = File.CreateText(filename);
}
else
{
sr = File.AppendText(filename);
}
sr.WriteLine(strMemo);
}
catch
{ }
finally
{
if (sr != null)
sr.Close();
}
}
//#endregion
#endregion #region 接收的消息实体类 以及 填充方法
private class ExmlMsg
{
/// <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; } } private ExmlMsg GetExmlMsg(XmlElement root)
{
ExmlMsg xmlMsg = new ExmlMsg() {
FromUserName = root.SelectSingleNode("FromUserName").InnerText,
ToUserName = root.SelectSingleNode("ToUserName").InnerText,
CreateTime = root.SelectSingleNode("CreateTime").InnerText,
MsgType = root.SelectSingleNode("MsgType").InnerText,
};
if (xmlMsg.MsgType.Trim().ToLower() == "text")
{
xmlMsg.Content = root.SelectSingleNode("Content").InnerText;
}
else if (xmlMsg.MsgType.Trim().ToLower() == "event")
{
xmlMsg.EventName = root.SelectSingleNode("Event").InnerText;
}
return xmlMsg;
}
#endregion
}
}

  

【最后说的】

  如果以上程序存在错误,欢迎指出,还是学生,水平有限,联系方式sina微博@导弹林瀚,谢谢。

  也懒得放github上了,如果要扩展或者二次开发,请看我上面的分析部分,该说的我都说了。

C# asp.net 搭建微信公众平台(可实现关注消息与消息自动回复)的代码以及我所遇到的问题的更多相关文章

  1. [c#]asp.net开发微信公众平台(6)阶段总结、服务搭建、接入

    经过前5篇,跟着一步步来的话,任何人都能搭建好一个能处理各种微信消息的框架了,总结一下最容易忽略的问题: 1.文本消息中可以使用换行符\n    : 2.微信发来的消息中带的那个长整型的时间,我们完全 ...

  2. [C#]asp.net开发微信公众平台----目录汇总-持续更新

    1.[c#]asp.net微信公众平台开发(1)数据库设计 2.[c#]asp.net微信公众平台开发(2)多层架构框架搭建和入口实现 3.[c#]asp.net微信公众平台开发(3)微信消息封装及反 ...

  3. asp.net开发微信公众平台----目录汇总-持续更新

    1.[c#]asp.net微信公众平台开发(1)数据库设计 2.[c#]asp.net微信公众平台开发(2)多层架构框架搭建和入口实现 3.[c#]asp.net微信公众平台开发(3)微信消息封装及反 ...

  4. [c#]asp.net开发微信公众平台(8)微信9大高级接口,自定义菜单

    前7篇把最基础的消息接收和回复全做完了,  也把高级接口的入口和分拆处理写好了空方法,  此篇接着介绍微信的9大高级接口, 并着重讲解其中的自定义菜单. 微信9大接口为: 1.语音识别接口 2.客服接 ...

  5. [c#]asp.net开发微信公众平台(7)前6篇的整体框架demo源码

    这里给出的demo是具备整体框架的微信公众平台源码, 所谓demo就是拿过去就可以直接演示使用的东西,  当然不会具备非常详细的具体到业务层面.数据层面的东西, 每个人都可以在此基础上自由发挥,  只 ...

  6. [c#]asp.net开发微信公众平台(5)微信图文消息

    上篇已经成功响应了关注事件,也实现了文本消息的发送,这篇开始图文消息处理, 微信中最常用的消息类型就是图文消息了,因为它图文并茂,最能表达信息. 图文消息在微信中的接口定义如下: <xml> ...

  7. 微信公众平台开发教程Java版(三) 消息接收和发送

    https://www.iteye.com/blog/tuposky-2017429 前面两章已经介绍了如何接入微信公众平台,这一章说说消息的接收和发送 可以先了解公众平台的消息api接口(接收消息, ...

  8. 使用python一步一步搭建微信公众平台(一)

    使用的工具,python 新浪SAE平台,微信的公众平台 你需要先在微信的公众平台与新浪SAE平台上各种注册,微信平台注册的时候需要你拍张手持身份证的照片,还有几天的审核期 微信公众平台:http:/ ...

  9. asp.net 实现微信公众平台的主动推送信息

    通过学习借鉴朋友的实现方法进行整理(微信公众帐号主动发送消息给用户,asp.net版本). /// <summary> /// MD5 32位加密 /// </summary> ...

随机推荐

  1. 再说表单验证,在Web Api中使用ModelState进行接口参数验证

    写在前面 上篇文章中说到了表单验证的问题,然后尝试了一下用扩展方法实现链式编程,评论区大家讨论的非常激烈也推荐了一些很强大的验证插件.其中一位园友提到了说可以使用MVC的ModelState,因为之前 ...

  2. redis实战(01)_redis安装

    早就想对redis进行实战操作了,最近看了一些视频和参考书籍,总结总结一下,redis实战内容: 实战前先对redis做一个大概的认识: 现在开始安装redis了... redis的安装下载地址 ht ...

  3. using 声明与编译指令

    using std::cout; // using 声明 using namespace std; // using 编译指令,导入std里面的所有名称 一般使用using 声明,它只会导入指定的名称 ...

  4. 【iBeacon】iBeacon前沿初探技术备忘

    iBeacon是工作在蓝牙4.0(BLE)硬件下的一种协议,属于蓝牙4.0广播协议的一种,通过该协议和一个蓝牙模块可以实现非接触的身份识别.位置检测等. How does BLE communicat ...

  5. Nodejs 的 Express框架 学习体会 补充中。。。

    最近为了用Shadow Socket FQ,到https://bandwagonhost.com上买了一个便宜的vps,19.99美元一年.服务器闲着也是闲着,就想搭建一个简单的博客. Express ...

  6. RIDE -- Robot Framework setup

    RobotFramework 是一款基于python 的可以实现关键字驱动和数据驱动并能够生成比较漂亮的测试报告的一款测试框架 这里使用的环境是 python-2.7.10.amd64.msi RID ...

  7. css3中变形函数(同样是对元素来说的)和元素通过改变自身属性达到动画效果

    /*对元素进行改变(移动.变形.伸缩.扭曲)*/ .wrapper{ margin:100px 100px auto auto; width:300px; height:200px; border:2 ...

  8. iOS block

    主要内容: block基本声明格式 block访问区域变量 block代替代理 block基本声明格式: ^(传入的参数){具体代码}; 注: Block实体开头是"^",接着是由 ...

  9. java类加载器加载文件

    例子:采用配置文件加反射的方式创建ArrayList和HashSet的实例对象. //第一种方式:类加载器加载文件 InputStream ips = ReflectTest2.class.getCl ...

  10. 第三天--html区块

    <!Doctype html><html>    <head>        <meta charset="utf-8">      ...