首页 > 代码库 > 微信在线信息模拟测试工具(基于Senparc.Weixin.MP)
微信在线信息模拟测试工具(基于Senparc.Weixin.MP)
目前为止似乎还没有看到过Web版的普通消息测试工具(除了官方针对高级接口的),现有的一些桌面版的几个测试工具也都是使用XML直接请求,非常不友好,我们来尝试做一个“面向对象”操作的测试工具。
测试工具在线DEMO:http://weixin.senparc.com/SimulateTool
Senparc.Weixin.MP是一个开源的微信SDK项目,地址:https://github.com/JeffreySu/WeiXinMPSDK (其中https://github.com/JeffreySu/WeiXinMPSDK/tree/master/Senparc.Weixin.MP.Sample 包含了本文所讲的所有源代码)
也可以通过Nuget直接安装到项目中:https://www.nuget.org/packages/Senparc.Weixin.MP
Senparc.Weixin.MP教程索引:http://www.cnblogs.com/szw/archive/2013/05/14/weixin-course-index.html
下面大致解释一下源代码及工作原理:
一、界面
界面分为4大区域:接口设置、发送参数、发送内容和接收内容
其中接口设置用于提供类似微信公众账号后台的Url和Token的对接参数设置,指定目标服务器。
在发送参数中,根据选择不同的消息类型,下面的参数选项会对应变化。
发送内容显示的是提交参数之后,模拟发送到目标服务器的XML,这里摆脱了之前一些需要手动输入XML的麻烦。
根据发送内容,在接收内容框中,显示目标服务器返回的实际内容。
二、服务器端代码
由于使用了Senparc.Weixin.MP SDK,所有的XML生成、代理操作、XML流等操作都变得非常简单,一共只用了100多行代码就实现了XML生成及模拟发送、接收等2大块功能,这里为了让大家看得更明白,将所有代码都尽量平铺直叙,实际还可以有很多缩减或重用的地方(文件位于源代码/Senparc.Weixin.MP.Sample/Senparc.Weixin.MP.Sample/Controllers/SimulateToolController.cs):
using System;using System.IO;using System.Web.Mvc;using System.Xml.Linq;using Senparc.Weixin.MP.Agent;using Senparc.Weixin.MP.Entities;using Senparc.Weixin.MP.Helpers;namespace Senparc.Weixin.MP.Sample.Controllers{ public class SimulateToolController : BaseController { /// <summary> /// 获取请求XML /// </summary> /// <returns></returns> private XDocument GetrequestMessaageDoc(string url, string token, RequestMsgType requestType, Event? eventType) { RequestMessageBase requestMessaage = null; switch (requestType) { case RequestMsgType.Text: requestMessaage = new RequestMessageText() { Content = Request.Form["Content"], }; break; case RequestMsgType.Location: requestMessaage = new RequestMessageLocation() { Label = Request.Form["Label"], Location_X = double.Parse(Request.Form["Location_X"]), Location_Y = double.Parse(Request.Form["Location_Y"]), Scale = int.Parse(Request.Form["Scale"]) }; break; case RequestMsgType.Image: requestMessaage = new RequestMessageImage() { PicUrl = Request.Form["PicUrl"], }; break; case RequestMsgType.Voice: requestMessaage = new RequestMessageVoice() { Format = Request.Form["Format"], Recognition = Request.Form["Recognition"], }; break; case RequestMsgType.Video: requestMessaage = new RequestMessageVideo() { MsgId = long.Parse(Request.Form["MsgId"]), ThumbMediaId = Request.Form["ThumbMediaId"], }; break; //case RequestMsgType.Link: // break; case RequestMsgType.Event: if (eventType.HasValue) { RequestMessageEventBase requestMessageEvent = null; switch (eventType.Value) { //case Event.ENTER: // break; case Event.LOCATION: requestMessageEvent = new RequestMessageEvent_Location() { Latitude = long.Parse(Request.Form["Event.Latitude"]), Longitude = long.Parse(Request.Form["Event.Longitude"]), Precision = double.Parse(Request.Form["Event.Precision"]) }; break; case Event.subscribe: requestMessageEvent = new RequestMessageEvent_Subscribe() { EventKey = Request.Form["Event.EventKey"] }; break; case Event.unsubscribe: requestMessageEvent = new RequestMessageEvent_Unsubscribe(); break; case Event.CLICK: requestMessageEvent = new RequestMessageEvent_Click() { EventKey = Request.Form["Event.EventKey"] }; break; case Event.scan: requestMessageEvent = new RequestMessageEvent_Scan() { EventKey = Request.Form["Event.EventKey"], Ticket = Request.Form["Event.Ticket"] }; break; case Event.VIEW: requestMessageEvent = new RequestMessageEvent_View() { EventKey = Request.Form["Event.EventKey"] }; break; case Event.MASSSENDJOBFINISH: requestMessageEvent = new RequestMessageEvent_MassSendJobFinish() { FromUserName = "mphelper",//系统指定 ErrorCount = int.Parse(Request.Form["Event.ErrorCount"]), FilterCount = int.Parse(Request.Form["Event.FilterCount"]), SendCount = int.Parse(Request.Form["Event.SendCount"]), Status = Request.Form["Event.Status"], TotalCount = int.Parse(Request.Form["Event.TotalCount"]) }; break; default: throw new ArgumentOutOfRangeException("eventType"); } requestMessaage = requestMessageEvent; } else { throw new ArgumentOutOfRangeException("eventType"); } break; default: throw new ArgumentOutOfRangeException("requestType"); } requestMessaage.CreateTime = DateTime.Now; requestMessaage.FromUserName = requestMessaage.FromUserName ?? "FromUserName(OpenId)";//用于区别不同的请求用户 requestMessaage.ToUserName = "ToUserName"; return requestMessaage.ConvertEntityToXml(); } /// <summary> /// 默认页面 /// </summary> /// <returns></returns> public ActionResult Index() { ViewData["Token"] = WeixinController.Token; return View(); } /// <summary> /// 模拟发送并返回结果 /// </summary> /// <returns></returns> [HttpPost] public ActionResult Index(string url, string token, RequestMsgType requestType, Event? eventType) { using (MemoryStream ms = new MemoryStream()) { var requestMessaageDoc = GetrequestMessaageDoc(url, token, requestType, eventType); requestMessaageDoc.Save(ms); ms.Seek(0, SeekOrigin.Begin); var responseMessageXml = MessageAgent.RequestXml(null, url, token, requestMessaageDoc.ToString()); return Content(responseMessageXml); } } /// <summary> /// 返回模拟发送的XML /// </summary> /// <returns></returns> [HttpPost] public ActionResult GetRequestMessageXml(string url, string token, RequestMsgType requestType, Event? eventType) { var requestMessaageDoc = GetrequestMessaageDoc(url, token, requestType, eventType); return Content(requestMessaageDoc.ToString()); } }}
三、View代码
下面是MVC中View(razor)的代码(200行左右,文件位于源代码/Senparc.Weixin.MP.Sample/Senparc.Weixin.MP.Sample/Views/SimulateTool/Index.cshtml):
1 @{ 2 ViewBag.Title = "微信消息模拟测试工具"; 3 Layout = "~/Views/Shared/_Layout.cshtml"; 4 5 var nonce = "JeffreySu"; 6 var timestamp = DateTime.Now.Ticks.ToString(); 7 var echostr = DateTime.Now.Ticks.ToString(); 8 var token = ViewData["Token"] as string; 9 } 10 @section HeaderContent 11 { 12 <style> 13 .param { 14 display: none; 15 } 16 17 .messageXmlArea { 18 width: 100%; 19 } 20 21 .messageXmlArea textarea { 22 width: 100%; 23 height: 200px; 24 } 25 26 .paramAreaLeft { 27 float: left; 28 width: 45%; 29 margin-right: 6%; 30 } 31 32 .paramArearight { 33 width: 45%; 34 float: left; 35 } 36 37 #requestType, #eventType { 38 padding: 5px; 39 } 40 </style> 41 <script> 42 $(function () { 43 $(‘#requestType‘).change(checkRequestType); 44 $(‘#eventType‘).change(checkEventType); 45 checkRequestType(); 46 checkEventType(); 47 }); 48 49 function checkRequestType() { 50 var requestType = $(‘#requestType‘).val(); 51 var paramId = ‘param‘ + requestType; 52 $(‘div[id^=param]‘).hide(); 53 $(‘#‘ + paramId).show(); 54 } 55 56 function checkEventType() { 57 var requestType = $(‘#eventType‘).val(); 58 var eventId = ‘event‘ + requestType; 59 $(‘div[id^=event]‘).hide(); 60 $(‘#‘ + eventId).show(); 61 } 62 63 function sendMessage() { 64 var url = $(‘#Url‘).val(); 65 var token = $(‘#Token‘).val(); 66 var requestType = $(‘#requestType‘).val(); 67 var eventType = $(‘#eventType‘).val(); 68 var param = { url: url, token: token, requestType: requestType }; 69 var paramId = ‘param‘ + requestType; 70 var eventId = ‘event‘ + eventType; 71 //设置参数 72 if (requestType != ‘Event‘) { 73 $.each($(‘#‘ + paramId).find(‘input‘), function (i, item) { 74 param[$(item).attr(‘name‘)] = $(item).val(); 75 }); 76 } else { 77 param.eventType = eventType; 78 $.each($(‘#‘ + eventId).find(‘input‘), function (i, item) { 79 param[$(item).attr(‘name‘)] = $(item).val(); 80 }); 81 } 82 83 var txtResponseMessageXML = $(‘#responseMessageXML‘); 84 var txtRequestMessageXML = $(‘#requestMessageXML‘); 85 86 txtResponseMessageXML.html(‘载入中...‘); 87 txtRequestMessageXML.html(‘载入中...‘); 88 89 $.post(‘@Url.Action("Index")‘, param, function (result) { 90 txtResponseMessageXML.html(result); 91 }); 92 93 $.post(‘@Url.Action("GetRequestMessageXml")‘, param, function (result) { 94 txtRequestMessageXML.html(result); 95 }); 96 } 97 </script> 98 } 99 @section Featured100 {101 102 }103 <section class="content-wrapper main-content clear-fix">104 <h1>消息模拟工具</h1>105 <div class="clear-fix"></div>106 <div id="simulateTool">107 <div class="paramAreaLeft">108 <h3>接口设置</h3>109 <div>110 URL:@Html.TextBox("Url", Url.Action("Index", "Weixin", null, "http", Request.Url.Host))<br />111 Token:@Html.TextBox("Token", token)112 </div>113 <h3>发送参数</h3>114 <div>115 类型:<select id="requestType">116 <option value="Text">文本</option>117 <option value="Location">地理位置</option>118 <option value="Image">图片</option>119 <option value="Voice">语音</option>120 <option value="Video">视频</option>121 @*<option value="Link">连接信息</option>*@122 <option value="Event">事件推送</option>123 </select>124 </div>125 <div>126 参数:127 <div id="paramText" class="param">128 Content:<input name="Content" />129 </div>130 <div id="paramLocation" class="param">131 Label:<input name="Label" /><br />132 Location_X:<input name="Location_X" type="number" value="0" /><br />133 Location_Y:<input name="Location_Y" type="number" value="0" /><br />134 Scale:<input name="Scale" type="number" value="0" step="1" /><br />135 </div>136 <div id="paramImage" class="param">137 PicUrl:<input name="PicUrl" /><br />138 </div>139 <div id="paramVoice" class="param">140 Format:<input name="Format" value="arm" /><br />141 Recognition:<input name="Recognition" /><br />142 </div>143 <div id="paramVideo" class="param">144 MsgId:<input name="MsgId" type="number" value="@DateTime.Now.Ticks" step="1" /><br />145 ThumbMediaId:<input name="ThumbMediaId" /><br />146 </div>147 @*<div id="paramLink" class="param"></div>*@148 <div id="paramEvent" class="param">149 事件类型:<select id="eventType">150 @*<option value="ENTER">进入会话</option>*@151 <option value="LOCATION">地理位置</option>152 <option value="subscribe">订阅</option>153 <option value="unsubscribe">取消订阅</option>154 <option value="CLICK">自定义菜单点击事件</option>155 <option value="scan">二维码扫描</option>156 <option value="VIEW">URL跳转</option>157 <option value="MASSSENDJOBFINISH">事件推送群发结果</option>158 </select>159 @*<div id="eventENTER" class="param"></div>*@160 <div id="eventLOCATION" class="param">161 Latitude:<input name="Event.Latitude" type="number" value="0"/><br />162 Longitude:<input name="Event.Longitude" type="number" value="0"/><br />163 Precision:<input name="Event.Precision" type="number" value="0"/><br />164 </div>165 <div id="eventsubscribe" class="param">166 EventKey:<input name="Event.EventKey" /><br />167 </div>168 <div id="eventunsubscribe" class="param"></div>169 <div id="eventCLICK" class="param">170 EventKey:<input name="Event.EventKey" /><br />171 </div>172 <div id="eventscan" class="param">173 EventKey:<input name="Event.EventKey" /><br />174 Ticket:<input name="Event.Ticket" /><br />175 </div>176 <div id="eventVIEW" class="param">177 EventKey:<input name="Event.EventKey" value="http://" /><br />178 </div>179 <div id="eventMASSSENDJOBFINISH" class="param">180 ErrorCount:<input name="Event.ErrorCount" type="number" value="0"/><br />181 FilterCount:<input name="Event.FilterCount" type="number" value="0"/><br />182 SendCount:<input name="Event.SendCount" type="number" value="0"/><br />183 Status:<input name="Event.Status"/><br />184 TotalCount:<input name="Event.TotalCount" type="number" value="0"/><br />185 </div>186 </div>187 <div>188 <input type="button" value="提交" onclick="sendMessage()" />189 </div>190 </div>191 </div>192 <div class="paramArearight">193 194 <div class="messageXmlArea">195 <h3>发送内容(根据参数自动生成)</h3>196 <textarea id="requestMessageXML" readonly="readonly"></textarea>197 </div>198 <div class="messageXmlArea">199 <h3>接收内容</h3>200 <textarea id="responseMessageXML"></textarea>201 </div>202 </div>203 </div>204 </section>
因为代码已经足够简单,所以不再一一详解,如果有任何问题可以在评论里面讨论,欢迎提各种建议!