CustomMessageHandler.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /*----------------------------------------------------------------
  2. Copyright (C) 2015 Senparc
  3. 文件名:CustomMessageHandler.cs
  4. 文件功能描述:自定义MessageHandler
  5. 创建标识:Senparc - 20150312
  6. ----------------------------------------------------------------*/
  7. using System;
  8. using System.Configuration;
  9. using System.IO;
  10. using System.Linq;
  11. using System.Text;
  12. using System.Web;
  13. using System.Web.Configuration;
  14. using Senparc.Weixin.MP.Agent;
  15. using Senparc.Weixin.Context;
  16. using Senparc.Weixin.MP.Entities;
  17. using Senparc.Weixin.MP.Entities.Request;
  18. using Senparc.Weixin.MP.MessageHandlers;
  19. using Senparc.Weixin.MP.Helpers;
  20. using Senparc.Weixin.MP.Sample.CommonService.Utilities;
  21. namespace Senparc.Weixin.MP.Sample.CommonService.CustomMessageHandler
  22. {
  23. /// <summary>
  24. /// 自定义MessageHandler
  25. /// 把MessageHandler作为基类,重写对应请求的处理方法
  26. /// </summary>
  27. public partial class CustomMessageHandler : MessageHandler<CustomMessageContext>
  28. {
  29. /*
  30. * 重要提示:v1.5起,MessageHandler提供了一个DefaultResponseMessage的抽象方法,
  31. * DefaultResponseMessage必须在子类中重写,用于返回没有处理过的消息类型(也可以用于默认消息,如帮助信息等);
  32. * 其中所有原OnXX的抽象方法已经都改为虚方法,可以不必每个都重写。若不重写,默认返回DefaultResponseMessage方法中的结果。
  33. */
  34. #if DEBUG
  35. string agentUrl = "http://localhost:12222/App/Weixin/4";
  36. string agentToken = "27C455F496044A87";
  37. string wiweihiKey = "CNadjJuWzyX5bz5Gn+/XoyqiqMa5DjXQ";
  38. #else
  39. //下面的Url和Token可以用其他平台的消息,或者到www.weiweihi.com注册微信用户,将自动在“微信营销工具”下得到
  40. private string agentUrl = WebConfigurationManager.AppSettings["WeixinAgentUrl"];//这里使用了www.weiweihi.com微信自动托管平台
  41. private string agentToken = WebConfigurationManager.AppSettings["WeixinAgentToken"];//Token
  42. private string wiweihiKey = WebConfigurationManager.AppSettings["WeixinAgentWeiweihiKey"];//WeiweihiKey专门用于对接www.Weiweihi.com平台,获取方式见:http://www.weiweihi.com/ApiDocuments/Item/25#51
  43. #endif
  44. private string appId = WebConfigurationManager.AppSettings["WeixinAppId"];
  45. private string appSecret = WebConfigurationManager.AppSettings["WeixinAppSecret"];
  46. public CustomMessageHandler(Stream inputStream, PostModel postModel, int maxRecordCount = 0)
  47. : base(inputStream, postModel, maxRecordCount)
  48. {
  49. //这里设置仅用于测试,实际开发可以在外部更全局的地方设置,
  50. //比如MessageHandler<MessageContext>.GlobalWeixinContext.ExpireMinutes = 3。
  51. WeixinContext.ExpireMinutes = 3;
  52. if (!string.IsNullOrEmpty(postModel.AppId))
  53. {
  54. appId = postModel.AppId;//通过第三方开放平台发送过来的请求
  55. }
  56. }
  57. public override void OnExecuting()
  58. {
  59. //测试MessageContext.StorageData
  60. if (CurrentMessageContext.StorageData == null)
  61. {
  62. CurrentMessageContext.StorageData = 0;
  63. }
  64. base.OnExecuting();
  65. }
  66. public override void OnExecuted()
  67. {
  68. base.OnExecuted();
  69. CurrentMessageContext.StorageData = ((int)CurrentMessageContext.StorageData) + 1;
  70. }
  71. /// <summary>
  72. /// 处理文字请求
  73. /// </summary>
  74. /// <returns></returns>
  75. public override IResponseMessageBase OnTextRequest(RequestMessageText requestMessage)
  76. {
  77. //TODO:这里的逻辑可以交给Service处理具体信息,参考OnLocationRequest方法或/Service/LocationSercice.cs
  78. //方法一(v0.1),此方法调用太过繁琐,已过时(但仍是所有方法的核心基础),建议使用方法二到四
  79. //var responseMessage =
  80. // ResponseMessageBase.CreateFromRequestMessage(RequestMessage, ResponseMsgType.Text) as
  81. // ResponseMessageText;
  82. //方法二(v0.4)
  83. //var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(RequestMessage);
  84. //方法三(v0.4),扩展方法,需要using Senparc.Weixin.MP.Helpers;
  85. //var responseMessage = RequestMessage.CreateResponseMessage<ResponseMessageText>();
  86. //方法四(v0.6+),仅适合在HandlerMessage内部使用,本质上是对方法三的封装
  87. //注意:下面泛型ResponseMessageText即返回给客户端的类型,可以根据自己的需要填写ResponseMessageNews等不同类型。
  88. var responseMessage = base.CreateResponseMessage<ResponseMessageText>();
  89. if (requestMessage.Content == null)
  90. {
  91. }
  92. else if (requestMessage.Content == "约束")
  93. {
  94. responseMessage.Content =
  95. @"您正在进行微信内置浏览器约束判断测试。您可以:
  96. <a href=""http://weixin.senparc.com/FilterTest/"">点击这里</a>进行客户端约束测试(地址:http://weixin.senparc.com/FilterTest/),如果在微信外打开将直接返回文字。
  97. 或:
  98. <a href=""http://weixin.senparc.com/FilterTest/Redirect"">点击这里</a>进行客户端约束测试(地址:http://weixin.senparc.com/FilterTest/Redirect),如果在微信外打开将重定向一次URL。";
  99. }
  100. else if (requestMessage.Content == "托管" || requestMessage.Content == "代理")
  101. {
  102. //开始用代理托管,把请求转到其他服务器上去,然后拿回结果
  103. //甚至也可以将所有请求在DefaultResponseMessage()中托管到外部。
  104. DateTime dt1 = DateTime.Now; //计时开始
  105. var responseXml = MessageAgent.RequestXml(this, agentUrl, agentToken, RequestDocument.ToString());
  106. //获取返回的XML
  107. //上面的方法也可以使用扩展方法:this.RequestResponseMessage(this,agentUrl, agentToken, RequestDocument.ToString());
  108. /* 如果有WeiweihiKey,可以直接使用下面的这个MessageAgent.RequestWeiweihiXml()方法。
  109. * WeiweihiKey专门用于对接www.weiweihi.com平台,获取方式见:http://www.weiweihi.com/ApiDocuments/Item/25#51
  110. */
  111. //var responseXml = MessageAgent.RequestWeiweihiXml(weiweihiKey, RequestDocument.ToString());//获取Weiweihi返回的XML
  112. DateTime dt2 = DateTime.Now; //计时结束
  113. //转成实体。
  114. /* 如果要写成一行,可以直接用:
  115. * responseMessage = MessageAgent.RequestResponseMessage(agentUrl, agentToken, RequestDocument.ToString());
  116. * 或
  117. *
  118. */
  119. var msg = string.Format("\r\n\r\n代理过程总耗时:{0}毫秒", (dt2 - dt1).Milliseconds);
  120. var agentResponseMessage = responseXml.CreateResponseMessage();
  121. if (agentResponseMessage is ResponseMessageText)
  122. {
  123. (agentResponseMessage as ResponseMessageText).Content += msg;
  124. }
  125. else if (agentResponseMessage is ResponseMessageNews)
  126. {
  127. (agentResponseMessage as ResponseMessageNews).Articles[0].Description += msg;
  128. }
  129. return agentResponseMessage;//可能出现多种类型,直接在这里返回
  130. }
  131. else if (requestMessage.Content == "测试" || requestMessage.Content == "退出")
  132. {
  133. /*
  134. * 这是一个特殊的过程,此请求通常来自于微微嗨(http://www.weiweihi.com)的“盛派网络小助手”应用请求(http://www.weiweihi.com/User/App/Detail/1),
  135. * 用于演示微微嗨应用商店的处理过程,由于微微嗨的应用内部可以单独设置对话过期时间,所以这里通常不需要考虑对话状态,只要做最简单的响应。
  136. */
  137. if (requestMessage.Content == "测试")
  138. {
  139. //进入APP测试
  140. responseMessage.Content = "您已经进入【盛派网络小助手】的测试程序,请发送任意信息进行测试。发送文字【退出】退出测试对话。10分钟内无任何交互将自动退出应用对话状态。";
  141. }
  142. else
  143. {
  144. //退出APP测试
  145. responseMessage.Content = "您已经退出【盛派网络小助手】的测试程序。";
  146. }
  147. }
  148. else if (requestMessage.Content == "AsyncTest")
  149. {
  150. //异步并发测试(提供给单元测试使用)
  151. DateTime begin = DateTime.Now;
  152. int t1, t2, t3;
  153. System.Threading.ThreadPool.GetAvailableThreads(out t1, out t3);
  154. System.Threading.ThreadPool.GetMaxThreads(out t2, out t3);
  155. System.Threading.Thread.Sleep(TimeSpan.FromSeconds(4));
  156. DateTime end = DateTime.Now;
  157. var thread = System.Threading.Thread.CurrentThread;
  158. responseMessage.Content = string.Format("TId:{0}\tApp:{1}\tBegin:{2:mm:ss,ffff}\tEnd:{3:mm:ss,ffff}\tTPool:{4}",
  159. thread.ManagedThreadId,
  160. HttpContext.Current != null ? HttpContext.Current.ApplicationInstance.GetHashCode() : -1,
  161. begin,
  162. end,
  163. t2 - t1
  164. );
  165. }
  166. else if(requestMessage.Content=="open")
  167. {
  168. var openResponseMessage = requestMessage.CreateResponseMessage<ResponseMessageNews>();
  169. openResponseMessage.Articles.Add(new Article()
  170. {
  171. Title = "开放平台微信授权测试",
  172. Description = @"点击进入Open授权页面。
  173. 授权之后,您的微信所收到的消息将转发到第三方(盛派网络小助手)的服务器上,并获得对应的回复。
  174. 测试完成后,您可以登陆公众号后台取消授权。",
  175. Url = "http://weixin.senparc.com/OpenOAuth/JumpToMpOAuth"
  176. });
  177. return openResponseMessage;
  178. }
  179. else
  180. {
  181. var result = new StringBuilder();
  182. result.AppendFormat("您刚才发送了文字信息:{0}\r\n\r\n", requestMessage.Content);
  183. if (CurrentMessageContext.RequestMessages.Count > 1)
  184. {
  185. result.AppendFormat("您刚才还发送了如下消息({0}/{1}):\r\n", CurrentMessageContext.RequestMessages.Count,
  186. CurrentMessageContext.StorageData);
  187. for (int i = CurrentMessageContext.RequestMessages.Count - 2; i >= 0; i--)
  188. {
  189. var historyMessage = CurrentMessageContext.RequestMessages[i];
  190. result.AppendFormat("{0} 【{1}】{2}\r\n",
  191. historyMessage.CreateTime.ToShortTimeString(),
  192. historyMessage.MsgType.ToString(),
  193. (historyMessage is RequestMessageText)
  194. ? (historyMessage as RequestMessageText).Content
  195. : "[非文字类型]"
  196. );
  197. }
  198. result.AppendLine("\r\n");
  199. }
  200. result.AppendFormat("如果您在{0}分钟内连续发送消息,记录将被自动保留(当前设置:最多记录{1}条)。过期后记录将会自动清除。\r\n",
  201. WeixinContext.ExpireMinutes, WeixinContext.MaxRecordCount);
  202. result.AppendLine("\r\n");
  203. result.AppendLine(
  204. "您还可以发送【位置】【图片】【语音】【视频】等类型的信息(注意是这几种类型,不是这几个文字),查看不同格式的回复。\r\nSDK官方地址:http://weixin.senparc.com");
  205. responseMessage.Content = result.ToString();
  206. }
  207. return responseMessage;
  208. }
  209. /// <summary>
  210. /// 处理位置请求
  211. /// </summary>
  212. /// <param name="requestMessage"></param>
  213. /// <returns></returns>
  214. public override IResponseMessageBase OnLocationRequest(RequestMessageLocation requestMessage)
  215. {
  216. var locationService = new LocationService();
  217. var responseMessage = locationService.GetResponseMessage(requestMessage as RequestMessageLocation);
  218. return responseMessage;
  219. }
  220. public override IResponseMessageBase OnShortVideoRequest(RequestMessageShortVideo requestMessage)
  221. {
  222. var responseMessage = this.CreateResponseMessage<ResponseMessageText>();
  223. responseMessage.Content = "您刚才发送的是小视频";
  224. return responseMessage;
  225. }
  226. /// <summary>
  227. /// 处理图片请求
  228. /// </summary>
  229. /// <param name="requestMessage"></param>
  230. /// <returns></returns>
  231. public override IResponseMessageBase OnImageRequest(RequestMessageImage requestMessage)
  232. {
  233. var responseMessage = CreateResponseMessage<ResponseMessageNews>();
  234. responseMessage.Articles.Add(new Article()
  235. {
  236. Title = "您刚才发送了图片信息",
  237. Description = "您发送的图片将会显示在边上",
  238. PicUrl = requestMessage.PicUrl,
  239. Url = "http://weixin.senparc.com"
  240. });
  241. responseMessage.Articles.Add(new Article()
  242. {
  243. Title = "第二条",
  244. Description = "第二条带连接的内容",
  245. PicUrl = requestMessage.PicUrl,
  246. Url = "http://weixin.senparc.com"
  247. });
  248. return responseMessage;
  249. }
  250. /// <summary>
  251. /// 处理语音请求
  252. /// </summary>
  253. /// <param name="requestMessage"></param>
  254. /// <returns></returns>
  255. public override IResponseMessageBase OnVoiceRequest(RequestMessageVoice requestMessage)
  256. {
  257. var responseMessage = CreateResponseMessage<ResponseMessageMusic>();
  258. //上传缩略图
  259. var accessToken = CommonAPIs.AccessTokenContainer.TryGetAccessToken(appId, appSecret);
  260. var uploadResult = AdvancedAPIs.MediaApi.UploadTemporaryMedia(accessToken, UploadMediaFileType.image,
  261. Server.GetMapPath("~/Images/Logo.jpg"));
  262. //设置音乐信息
  263. responseMessage.Music.Title = "天籁之音";
  264. responseMessage.Music.Description = "播放您上传的语音";
  265. responseMessage.Music.MusicUrl = "http://weixin.senparc.com/Media/GetVoice?mediaId=" + requestMessage.MediaId;
  266. responseMessage.Music.HQMusicUrl = "http://weixin.senparc.com/Media/GetVoice?mediaId=" + requestMessage.MediaId;
  267. responseMessage.Music.ThumbMediaId = uploadResult.media_id;
  268. return responseMessage;
  269. }
  270. /// <summary>
  271. /// 处理视频请求
  272. /// </summary>
  273. /// <param name="requestMessage"></param>
  274. /// <returns></returns>
  275. public override IResponseMessageBase OnVideoRequest(RequestMessageVideo requestMessage)
  276. {
  277. var responseMessage = CreateResponseMessage<ResponseMessageText>();
  278. responseMessage.Content = "您发送了一条视频信息,ID:" + requestMessage.MediaId;
  279. return responseMessage;
  280. }
  281. /// <summary>
  282. /// 处理链接消息请求
  283. /// </summary>
  284. /// <param name="requestMessage"></param>
  285. /// <returns></returns>
  286. public override IResponseMessageBase OnLinkRequest(RequestMessageLink requestMessage)
  287. {
  288. var responseMessage = ResponseMessageBase.CreateFromRequestMessage<ResponseMessageText>(requestMessage);
  289. responseMessage.Content = string.Format(@"您发送了一条连接信息:
  290. Title:{0}
  291. Description:{1}
  292. Url:{2}", requestMessage.Title, requestMessage.Description, requestMessage.Url);
  293. return responseMessage;
  294. }
  295. /// <summary>
  296. /// 处理事件请求(这个方法一般不用重写,这里仅作为示例出现。除非需要在判断具体Event类型以外对Event信息进行统一操作
  297. /// </summary>
  298. /// <param name="requestMessage"></param>
  299. /// <returns></returns>
  300. public override IResponseMessageBase OnEventRequest(IRequestMessageEventBase requestMessage)
  301. {
  302. var eventResponseMessage = base.OnEventRequest(requestMessage);//对于Event下属分类的重写方法,见:CustomerMessageHandler_Events.cs
  303. //TODO: 对Event信息进行统一操作
  304. return eventResponseMessage;
  305. }
  306. public override IResponseMessageBase DefaultResponseMessage(IRequestMessageBase requestMessage)
  307. {
  308. /* 所有没有被处理的消息会默认返回这里的结果,
  309. * 因此,如果想把整个微信请求委托出去(例如需要使用分布式或从其他服务器获取请求),
  310. * 只需要在这里统一发出委托请求,如:
  311. * var responseMessage = MessageAgent.RequestResponseMessage(agentUrl, agentToken, RequestDocument.ToString());
  312. * return responseMessage;
  313. */
  314. var responseMessage = this.CreateResponseMessage<ResponseMessageText>();
  315. responseMessage.Content = "这条消息来自DefaultResponseMessage。";
  316. return responseMessage;
  317. }
  318. }
  319. }