[开源]微信在线信息模拟测试工具(基于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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
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 Featured
100 {
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>
复制代码

 

  因为代码已经足够简单,所以不再一一详解,如果有任何问题可以在评论里面讨论,欢迎提各种建议!

posted on   SZW  阅读(19168)  评论(13编辑  收藏  举报
编辑推荐:
· 现代计算机视觉入门之:什么是视频
· 你所不知道的 C/C++ 宏知识
· 聊一聊 操作系统蓝屏 c0000102 的故障分析
· SQL Server 内存占用高分析
· .NET Core GC计划阶段(plan_phase)底层原理浅谈
阅读排行:
· 盘点!HelloGitHub 年度热门开源项目
· 某Websocket反爬逆向分析+请求加解密+还原html
· 02现代计算机视觉入门之:什么是视频
· 回顾我的软件开发经历:我与代码生成器的涅槃之路
· DeepSeek V3 两周使用总结
< 2025年1月 >
29 30 31 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 1
2 3 4 5 6 7 8

点击右上角即可分享
微信分享提示