第一次写文,主要用来记录一些之前遇到的问题和解决方法。有写的不好的地方 请(咬) 见(我) 谅(啊)
使用工具:VS2017
环境:.NET4.5 Web API dapper
跟这个视频一步一步学习的微信公众号快速开发教程-学习视频教程-腾讯课堂(他用的java后台)
视频中的文章:
微信公众号开发教程(一) 验证接入 - 简书 (一) 验证接入
微信公众号开发教程(二)消息接收与响应处理 - 简书(二)消息接收与响应处理
微信公众号开发教程(三)事件推送及关键字回复 - 简书(三)事件推送及关键字回复
https://www.jianshu.com/p/85573685f17d(四)自定义菜单
微信公众号开发教程(五)发送模板消息 - 简书 (五)发送模板消息
微信公众号开发教程(六)获取微信用户信息-网页授权 - 简书 (六)获取微信用户信息-网页授权
微信公众号开发教程(七)JSSDK-监听分享朋友圈事件 - 简书 (七)JSSDK-监听分享朋友圈事件
开始:
第一步:创建Web Api。本来打算使用.net4.0 的 不知道什么原因 在web 项目中没有找到 webapi 选项,就改用了.NET4.5
第二步:如何接入,如何配置接口地址,如何做内网穿透等 请参考上面的文章连接 和视频资料,这里不做赘述;
添加一个 ApiController。添加一个Get方法(用来验证接入接口是否可用)一个Post方法(用来处理业务)对外接口就这两个就够了。
下载 token验证代码:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419318479&token=&lang=zh_CN 根据这个文章中的提示 下载 加密解密的代码,验证接口是要用到
上代码(直接上图片,代码不多手打一下吧,还能加深印象):
其中:Gloabls.Token 是你在 微信接口配置信息那填写的Token
第三步:处理 用户 提交的一些信息(使用Post提交)
直接上代码:
/// <summary>
/// 微信POST的消息,消息内容在content中
/// </summary>
/// <returns></returns>
[HttpPost]
public HttpResponseMessage Post()
{
var requestContent = Request.Content.ReadAsStreamAsync().Result;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(requestContent);
string msgTypeStr = xmlDoc.SelectSingleNode("xml/MsgType").InnerText;//消息类型
string userName = xmlDoc.SelectSingleNode("xml/FromUserName").InnerText;//发送人
string efhName = xmlDoc.SelectSingleNode("xml/ToUserName").InnerText;//接受人
string createTime = xmlDoc.SelectSingleNode("xml/CreateTime").InnerText;//时间戳
string MsgId = msgTypeStr == "event" ? "" : xmlDoc.SelectSingleNode("xml/MsgId").InnerText;//消息ID,事件中没有
//验证消息是否重复
if (Removal(msgTypeStr, MsgId, userName, createTime))
{
return null;
}
switch (msgTypeStr)
{
case "text":
{
//这里处理能识别的消息
var content = xmlDoc.SelectSingleNode("xml/Content").InnerText;
//根据不同消息查询相应的回复//这里是你自己的业务了,不要偷懒
dbo_message msg = bllMsg.GetMessage(content, userName);
switch (msg.Msg_type)
{
case "text":
return SendText(userName, efhName, msg.Msg_str);
case "news":
{
//整理数据并返回
List<Article> list = new List<Article>();
list.Add(new Article()
{
Description = msg.Description,
PicUrl = msg.PicUrl,
Title = msg.Title,
Url = msg.Url
});
return SendNews(userName, efhName, list);
}
default:
return SendText(userName, efhName, IncomprehensionText());
}
}
case "image":
var imgId = xmlDoc.SelectSingleNode("xml/MediaId").InnerText;
return SendImage(userName, efhName, imgId);
case "event":
//事件处理比较多
return EvnetHandle(xmlDoc, userName, efhName);
default:
{
return SendText(userName, efhName, IncomprehensionText());
}
}
}
/// <summary>
/// 验证消息是否重复如果重复 返回true
/// </summary>
/// <param name="msgTypeStr">消息类型</param>
/// <param name="MsgId">消息编号</param>
/// <param name="userName">发送人Id</param>
/// <param name="createTime">时间戳</param>
/// <returns></returns>
private static bool Removal(string msgTypeStr, string MsgId, string userName, string createTime)
{
if (string.IsNullOrWhiteSpace(MsgId))
{
MsgId = userName + createTime;
}
//删除3分钟之前的消息
foreach (var item in _msgId.ToList())
{
if (item.Value < DateTime.Now.AddMinutes(-3))
{
_msgId.Remove(item.Key);
}
}
//验证消息是否存在
if (_msgId.Keys.Contains(MsgId))
{
return true;
}
else
{
_msgId[MsgId] = DateTime.Now;
}
return false;
}
private static string IncomprehensionText()
{
return "我还没长大,不明白你在说什么?*✧⁺˚⁺ପ(๑・ω・)੭ु⁾⁾ 好好学习天天向上";
}
/// <summary>
/// 事件处理(关注,点击按钮等)
/// </summary>
/// <param name="xmlDoc"></param>
/// <param name="userName"></param>
/// <param name="efhName"></param>
/// <returns></returns>
private static HttpResponseMessage EvnetHandle(XmlDocument xmlDoc, string userName, string efhName)
{
var eve = xmlDoc.SelectSingleNode("xml/Event").InnerText;
switch (eve)
{
case "subscribe":
return SendText(userName, efhName, "谢谢观看");
case "CLICK":
//获取按钮事件的key值,用于处理相关业务
var eventKey = xmlDoc.SelectSingleNode("xml/EventKey").InnerText;
if (eventKey == "OTHER_CLICK")//这些是创建的按钮Key
{
return SendText(userName, efhName, "谢谢观看");
}
else if (eventKey == "BUY_COURSE")//这里也是按钮Key
{
return SendText(userName, efhName, "谢谢观看!自己要回复的 消息");
}
else
{
return SendText(userName, efhName, IncomprehensionText());
}
break;
case "VIEW":
var viewKey = xmlDoc.SelectSingleNode("xml/EventKey").InnerText;
return null;
break;
default:
return SendText(userName, efhName, IncomprehensionText());
break;
}
}
/// <summary>
/// 发送图片消息
/// </summary>
/// <param name="userName">用户</param>
/// <param name="efhName">公众号</param>
/// <param name="imgId">图片Id</param>
/// <returns></returns>
private static HttpResponseMessage SendImage(string userName, string efhName, string imgId)
{
string responseContent = XmlHelper.XmlSerialize<ImageMsg>(new ImageMsg
{
FromUserName = efhName,
Image = new ImageInfo(imgId),
ToUserName = userName
});
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(responseContent, Encoding.UTF8, "application/xml"),
};
}
/// <summary>
/// 发送文本消息
/// </summary>
/// <param name="userName">用户</param>
/// <param name="efhName">公众号</param>
/// <param name="msg">消息内容</param>
/// <returns></returns>
private static HttpResponseMessage SendText(string userName, string efhName, string msg)
{
string responseContent = XmlHelper.XmlSerialize<TxtMsg>(new TxtMsg
{
FromUserName = efhName,
Content = msg,
ToUserName = userName
});
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(responseContent, Encoding.UTF8, "application/xml"),
};
}
/// <summary>
/// 发送图文信息
/// </summary>
/// <param name="userName"></param>
/// <param name="efhName"></param>
/// <param name="msg"></param>
/// <returns></returns>
private static HttpResponseMessage SendNews(string userName, string efhName, List<Article> articles)
{
string responseContent = XmlHelper.XmlSerialize<News>(new News
{
FromUserName = efhName,
ArticleCount = articles.Count,
Articles = articles,
ToUserName = userName
});
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(responseContent, Encoding.UTF8, "application/xml"),
};
}
这个纯代码爽不爽,这里主要是处理一些 用户发送的文本消息,和功能按钮事件具体的也 需要你自己写了,这里主要整理一些 发送消息的方法,方便发送(文本,图片,图文)。
第四步:回复的消息处理。
微信接受消息是以xml格式发送的 ,使用序列化的时候需要注意 ,正常序列化会出现 对应 类型的 namespace 微信识别不出来,所以需要去掉这些,并且 是以<xml></xml>标签包裹消息,所以实体类上需要用 [XmlRoot("xml")]标注:
又是图片(哈哈哈,主要是图片看着更清晰)。一定要有 无参的构造函数,否则序列化时会报错(别问我怎么知道的):
文本消息:
图片消息:
图文消息:
对了,还有xml序列化的处理:
到这里基本上就可以用了!!是不是也没多少东西。
为避免有些看文章 跳过的(我自己就是)特在这里做一些提醒: WXBizMsgCrypt.GenarateSinature 如果没有找到这个方法请到第二步 token验证 步骤的地址中 去下载 代码(有源码哦)
还有一些引用到的
微信功能按钮创建 都是通用的,可在文章最上面的连接中找到。
加粗备注:回复的文本消息有字数限制(具体多少官方没有给出,我也没有验证),如果无故提示服务器异常的 请考虑是不是字数过多。字数过多的使用 微信的"客服消息"接口实现(这里也有字数限制,但是可以分成多个消息回复)。
正常消息可以在回复前检查一下字数,如果过长 直接返回null。或请等待 的字样。然后启动线程 把正确的消息分批回复
附上一段我自己的代码(具体业务 自己再去整理把,没有相应的dbo_enroll_course;其中的Gloabls.AccessToken为access_token;Gloabls.appId 和Gloabls.appSecret 在微信公众号管理中找吧):
/// <summary>
/// 过长消息回复代码
/// </summary>
/// <param name="enrolls"></param>
/// <param name="userName"></param>
private static void MoreMsg(List<dbo_enroll_course> enrolls, string userName)
{
Thread thread = new Thread(() =>
{
if (string.IsNullOrWhiteSpace(Gloabls.AccessToken))
{
Gloabls.AccessToken = dalToken.GetToken();
}
foreach (var item in enrolls)
{
again:
string msg = JsonConvert.SerializeObject(new
{
touser = userName,
msgtype = "text",
text = new { content = string.Format("姓名:{0}\r\n手机号:{1}\r\n购买时间:{2}\r\n购买课程:{3}\r\n教学方式:{4}\r\n套餐名称:{5}\r\n套餐信息:{6}\r\n当前状态:{7}\r\n\r\n\r\n", item.Enroll_Name, item.Enroll_Phone, item.Create_Time, item.Course_Name, item.Course_Qulity, item.Course_Group, item.Course_Group_Des, item.State == 9 ? "已为您安排好课程,请到“ClassIn”APP中查看" : "我们的业务人员正在积极为您安排课程,请耐心等待") }
});
string rest = HttpHelper.RequestData("https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + Gloabls.AccessToken, msg);
dynamic dy = JsonConvert.DeserializeObject<dynamic>(rest);
if (dy.errcode == "40001")
{
string res = HttpHelper.HttpGet(string.Format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={0}&secret={1}", Gloabls.appId, Gloabls.appSecret), "");
dynamic getdy = JsonConvert.DeserializeObject<dynamic>(res);
try
{
//把获取的access_token保存到数据
dalToken.UpdateTocke(getdy.access_token.Value);
Gloabls.AccessToken = getdy.access_token.Value;
goto again;
}
catch (System.Exception)
{
break;
}
}
}
});
thread.Start();
}
结束!!!谢谢观看,有什么好的建议请留言。
新增:使用测试公众号 开发的时候会有一个小问题,图文消息有时候微信会在 后面加上 一个参数 subscene=131。会导致404错误,可以重新关注 进行测试,如果电脑版微信没有问题,就可以忽略这个东西。正式版的公众号还没有试,试过后在写上来