目前为止似乎还没有看到过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):

 @{
ViewBag.Title = "微信消息模拟测试工具";
Layout = "~/Views/Shared/_Layout.cshtml"; var nonce = "JeffreySu";
var timestamp = DateTime.Now.Ticks.ToString();
var echostr = DateTime.Now.Ticks.ToString();
var token = ViewData["Token"] as string;
}
@section HeaderContent
{
<style>
.param {
display: none;
} .messageXmlArea {
width: 100%;
} .messageXmlArea textarea {
width: 100%;
height: 200px;
} .paramAreaLeft {
float: left;
width: 45%;
margin-right: 6%;
} .paramArearight {
width: 45%;
float: left;
} #requestType, #eventType {
padding: 5px;
}
</style>
<script>
$(function () {
$('#requestType').change(checkRequestType);
$('#eventType').change(checkEventType);
checkRequestType();
checkEventType();
}); function checkRequestType() {
var requestType = $('#requestType').val();
var paramId = 'param' + requestType;
$('div[id^=param]').hide();
$('#' + paramId).show();
} function checkEventType() {
var requestType = $('#eventType').val();
var eventId = 'event' + requestType;
$('div[id^=event]').hide();
$('#' + eventId).show();
} function sendMessage() {
var url = $('#Url').val();
var token = $('#Token').val();
var requestType = $('#requestType').val();
var eventType = $('#eventType').val();
var param = { url: url, token: token, requestType: requestType };
var paramId = 'param' + requestType;
var eventId = 'event' + eventType;
//设置参数
if (requestType != 'Event') {
$.each($('#' + paramId).find('input'), function (i, item) {
param[$(item).attr('name')] = $(item).val();
});
} else {
param.eventType = eventType;
$.each($('#' + eventId).find('input'), function (i, item) {
param[$(item).attr('name')] = $(item).val();
});
} var txtResponseMessageXML = $('#responseMessageXML');
var txtRequestMessageXML = $('#requestMessageXML'); txtResponseMessageXML.html('载入中...');
txtRequestMessageXML.html('载入中...'); $.post('@Url.Action("Index")', param, function (result) {
txtResponseMessageXML.html(result);
}); $.post('@Url.Action("GetRequestMessageXml")', param, function (result) {
txtRequestMessageXML.html(result);
});
}
</script>
}
@section Featured
{ }
<section class="content-wrapper main-content clear-fix">
<h1>消息模拟工具</h1>
<div class="clear-fix"></div>
<div id="simulateTool">
<div class="paramAreaLeft">
<h3>接口设置</h3>
<div>
URL:@Html.TextBox("Url", Url.Action("Index", "Weixin", null, "http", Request.Url.Host))<br />
Token:@Html.TextBox("Token", token)
</div>
<h3>发送参数</h3>
<div>
类型:<select id="requestType">
<option value="Text">文本</option>
<option value="Location">地理位置</option>
<option value="Image">图片</option>
<option value="Voice">语音</option>
<option value="Video">视频</option>
@*<option value="Link">连接信息</option>*@
<option value="Event">事件推送</option>
</select>
</div>
<div>
参数:
<div id="paramText" class="param">
Content:<input name="Content" />
</div>
<div id="paramLocation" class="param">
Label:<input name="Label" /><br />
Location_X:<input name="Location_X" type="number" value="0" /><br />
Location_Y:<input name="Location_Y" type="number" value="0" /><br />
Scale:<input name="Scale" type="number" value="0" step="1" /><br />
</div>
<div id="paramImage" class="param">
PicUrl:<input name="PicUrl" /><br />
</div>
<div id="paramVoice" class="param">
Format:<input name="Format" value="arm" /><br />
Recognition:<input name="Recognition" /><br />
</div>
<div id="paramVideo" class="param">
MsgId:<input name="MsgId" type="number" value="@DateTime.Now.Ticks" step="1" /><br />
ThumbMediaId:<input name="ThumbMediaId" /><br />
</div>
@*<div id="paramLink" class="param"></div>*@
<div id="paramEvent" class="param">
事件类型:<select id="eventType">
@*<option value="ENTER">进入会话</option>*@
<option value="LOCATION">地理位置</option>
<option value="subscribe">订阅</option>
<option value="unsubscribe">取消订阅</option>
<option value="CLICK">自定义菜单点击事件</option>
<option value="scan">二维码扫描</option>
<option value="VIEW">URL跳转</option>
<option value="MASSSENDJOBFINISH">事件推送群发结果</option>
</select>
@*<div id="eventENTER" class="param"></div>*@
<div id="eventLOCATION" class="param">
Latitude:<input name="Event.Latitude" type="number" value="0"/><br />
Longitude:<input name="Event.Longitude" type="number" value="0"/><br />
Precision:<input name="Event.Precision" type="number" value="0"/><br />
</div>
<div id="eventsubscribe" class="param">
EventKey:<input name="Event.EventKey" /><br />
</div>
<div id="eventunsubscribe" class="param"></div>
<div id="eventCLICK" class="param">
EventKey:<input name="Event.EventKey" /><br />
</div>
<div id="eventscan" class="param">
EventKey:<input name="Event.EventKey" /><br />
Ticket:<input name="Event.Ticket" /><br />
</div>
<div id="eventVIEW" class="param">
EventKey:<input name="Event.EventKey" value="http://" /><br />
</div>
<div id="eventMASSSENDJOBFINISH" class="param">
ErrorCount:<input name="Event.ErrorCount" type="number" value="0"/><br />
FilterCount:<input name="Event.FilterCount" type="number" value="0"/><br />
SendCount:<input name="Event.SendCount" type="number" value="0"/><br />
Status:<input name="Event.Status"/><br />
TotalCount:<input name="Event.TotalCount" type="number" value="0"/><br />
</div>
</div>
<div>
<input type="button" value="提交" onclick="sendMessage()" />
</div>
</div>
</div>
<div class="paramArearight"> <div class="messageXmlArea">
<h3>发送内容(根据参数自动生成)</h3>
<textarea id="requestMessageXML" readonly="readonly"></textarea>
</div>
<div class="messageXmlArea">
<h3>接收内容</h3>
<textarea id="responseMessageXML"></textarea>
</div>
</div>
</div>
</section>

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

[开源]微信在线信息模拟测试工具(基于Senparc.Weixin.MP开发)的更多相关文章

  1. 微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引

    Senparc.Weixin.MP SDK从一开始就坚持开源的状态,这个过程中得到了许多朋友的认可和支持. 目前SDK已经达到比较稳定的版本,这个过程中我觉得有必要整理一些思路和经验,和大家一起分享. ...

  2. 微信公众平台C# SDK:Senparc.Weixin.MP.dll

    https://github.com/Senparc/WeiXinMPSDK [转] http://www.cnblogs.com/szw/archive/2013/01/13/senparc-wei ...

  3. 【转】微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引

    微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引 Senparc.Weixin.MP SDK从一开始就坚持开源的状态,这个过程中得到了许多朋友的认可和支持. 目前SDK已经达到 ...

  4. 在线GET/POST API接口请求模拟测试工具

    在前后端开发过程中经常需要对HTTP接口进行测试,推荐几款比较好用的测试工具 Postman https://www.getpostman.com/ 强大的HTTP请求测试工具 支持多平台 Advan ...

  5. 用c#开发微信 (4) 基于Senparc.Weixin框架的接收事件推送处理 (源码下载)

    本文讲述使用Senparc.Weixin框架来快速处理各种接收事件推送.这里的消息指的是传统的微信公众平台消息交互,微信用户向公众号发送消息后,公众号回复消息给微信用户.包括以下类型: 1 subsc ...

  6. Senparc.Weixin.MP SDK 微信公众平台开发教程(二):成为开发者

    Senparc.Weixin.MP SDK 微信公众平台开发教程(二):成为开发者 这一篇主要讲作为一名使用公众平台接口的开发者,你需要知道的一些东西.其中也涉及到一些微信官方的规定或比较掩蔽的注意点 ...

  7. Senparc.Weixin.MP SDK 微信公众平台开发教程(二十一):在小程序中使用 WebSocket (.NET Core)

    本文将介绍如何在 .NET Core 环境下,借助 SignalR 在小程序内使用 WebSocket.关于 WebSocket 和 SignalR 的基础理论知识不在这里展开,已经有足够的参考资料, ...

  8. Senparc.Weixin.MP SDK 微信公众平台开发教程(十七):个性化菜单接口说明

    前不久微信上线了个性化菜单接口,Senparc.Weixin SDK也已经同步更新. 本次更新升级Senparc.Weixin.MP版本到v13.5.2,依赖Senparc.Weixin版本4.5.4 ...

  9. Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK

    Senparc.Weixin.MP SDK已经涵盖了微信6.x的所有公共API. 整个项目的源代码以及已经编译好的程序集可以在这个项目中获取到:https://github.com/JeffreySu ...

随机推荐

  1. K/3 Cloud开发之旅--环境准备篇

    K/3 Cloud是金蝶软件新推出的一款产品,介绍我就不多说了,谁用谁知道啊,那么我们如果要基于它做开发需要什么环境呢 开发环境必备软件 1 操作系统Windows X86/X64 或者Windows ...

  2. 虚拟机上安装Linux操作系统

    很久之前就知道虚拟机这个东西,也都在虚拟机上安装过Windows的操作系统和Linux的操作系统,但是一直都没有去做笔记. 最近还是比较有时间,就移除了前两天刚刚安装的Linux系统,重新安装一次,做 ...

  3. python中的enumerate

    读取一个iter,返回[index,内容]的一个list

  4. 关于UltraEdit的两个小问题

    问题一:如何让Java在编写过程中的关键字着色? 首先,需要把编辑的文件名字后缀更改为.java; 然后,找到UltraEdit的安装目录下的wordfile文件夹(以前是一个wordfile.txt ...

  5. Rational.Rose.Enterprise.v7.0 (2007)安装分享

    很多人都在找rational软件,很多都是2003的,有的宣称是2007,但结果还是2003.也许真的不存在Rational.Rose 2007,不过有IBM.Rational.Rose.Enterp ...

  6. Oracle数据库备份 expdp/impdp导出导入命令

    使用EXPDP和IMPDP时应该注意的事项: EXP和IMP是客户端工具程序,它们既可以在客户端使用,也可以在服务端使用. EXPDP和IMPDP是服务端的工具程序,他们只能在ORACLE服务端使用, ...

  7. 工具fiddler学习

    1:Fiddler 是以代理web服务器的形式工作的,它使用代理地址:127.0.0.1, 端口:8888. 当Fiddler会自动设置代理.能支持HTTP代理的任意程序的数据包都能被Fiddler嗅 ...

  8. IPv4头部结构

    2.2 IPv4头部结构 2.2.1 IPv4头部结构 IPv4的头部结构如图2-1所示.其长度通常为20字节,除非含有可变长的选项部分. 4位版本号(version)指定IP协议的版本.对IPv4来 ...

  9. bower

    1. bower介绍 Bower 是 twitter 推出的一款包管理工具,基于nodejs的模块化思想,把功能分散到各个模块中,让模块和模块之间存在联系,通过 Bower 来管理模块间的这种联系. ...

  10. btn-default

    Bootstrap 还有一种属于按钮的 class 属性叫做btn-default . to