[开源]微信在线信息模拟测试工具(基于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):
@{
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开发)的更多相关文章
- 微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引
Senparc.Weixin.MP SDK从一开始就坚持开源的状态,这个过程中得到了许多朋友的认可和支持. 目前SDK已经达到比较稳定的版本,这个过程中我觉得有必要整理一些思路和经验,和大家一起分享. ...
- 微信公众平台C# SDK:Senparc.Weixin.MP.dll
https://github.com/Senparc/WeiXinMPSDK [转] http://www.cnblogs.com/szw/archive/2013/01/13/senparc-wei ...
- 【转】微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引
微信公众账号 Senparc.Weixin.MP SDK 开发教程 索引 Senparc.Weixin.MP SDK从一开始就坚持开源的状态,这个过程中得到了许多朋友的认可和支持. 目前SDK已经达到 ...
- 在线GET/POST API接口请求模拟测试工具
在前后端开发过程中经常需要对HTTP接口进行测试,推荐几款比较好用的测试工具 Postman https://www.getpostman.com/ 强大的HTTP请求测试工具 支持多平台 Advan ...
- 用c#开发微信 (4) 基于Senparc.Weixin框架的接收事件推送处理 (源码下载)
本文讲述使用Senparc.Weixin框架来快速处理各种接收事件推送.这里的消息指的是传统的微信公众平台消息交互,微信用户向公众号发送消息后,公众号回复消息给微信用户.包括以下类型: 1 subsc ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二):成为开发者
Senparc.Weixin.MP SDK 微信公众平台开发教程(二):成为开发者 这一篇主要讲作为一名使用公众平台接口的开发者,你需要知道的一些东西.其中也涉及到一些微信官方的规定或比较掩蔽的注意点 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二十一):在小程序中使用 WebSocket (.NET Core)
本文将介绍如何在 .NET Core 环境下,借助 SignalR 在小程序内使用 WebSocket.关于 WebSocket 和 SignalR 的基础理论知识不在这里展开,已经有足够的参考资料, ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(十七):个性化菜单接口说明
前不久微信上线了个性化菜单接口,Senparc.Weixin SDK也已经同步更新. 本次更新升级Senparc.Weixin.MP版本到v13.5.2,依赖Senparc.Weixin版本4.5.4 ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(五):使用Senparc.Weixin.MP SDK
Senparc.Weixin.MP SDK已经涵盖了微信6.x的所有公共API. 整个项目的源代码以及已经编译好的程序集可以在这个项目中获取到:https://github.com/JeffreySu ...
随机推荐
- CentOS 7下设置DNS服务器
在CentOS 7下,手工设置 /etc/resolv.conf 里的DNS,过了一会,发现被系统重新覆盖或者清除了.和CentOS 6下的设置DNS方法不同,有几种方式: 1.使用全新的命令行工具 ...
- Ubuntu 16.04 下使用Xampp
Ubuntu 16.04 下使用Xampp 什么是Xampp? XAMPP(Apache+MySQL+PHP+PERL) 是一个功能强大的建站集成软件包.这个软件包原来的名字是 LAMPP,但是为了避 ...
- 使用IConfigurationSectionHandler在web.config中增加自定义配置
一. 场景 这里仅举一个简单应用的例子,我希望在web.config里面增加网站的基本信息,如:网站名称,网站版本号,是否将网站暂时关闭等.二. 基本实现方法1. 定义配置节点对应的类:Site ...
- 如何删除选中的checkbox
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Python 学习---------Day3
第七章 字符串单双引号字符串是一样的用转义序列代表特殊字节字符串抑制转义myfile=open(r'C:\new\text.dat','w')三重引号编写多行字符串块字符串更大的编码集std(u'sp ...
- 算法入门笔记------------Day1
1.C语言使用%d显示float值,不会把float值转换为近似的int值,而是显示垃圾值,使用%f显示int值,也不会把该int值转换为浮点值 2.三位数反转:输入一个三位数,分离它的百位,十位和个 ...
- 解决hadoop启动后datanode无法启动问题
hadoop部署完成后datanode无法启动问题解决 1.检查是否有遗留的hadoop进程还在运行,如果有的话,先stop-all.sh或kill杀掉: 2.在master节点上,删除/tmp/ha ...
- NSDate
NSDate : NSDate *date = [NSDate date];获取当前日期 NSDate 可以进行比较,通过earlierDate:方法获取二个日期中最早的. NSDate 通过late ...
- leetcode Insertion Sort List
题目:Sort a linked list using insertion sort. 代码: /** * Definition for singly-linked list. * struct Li ...
- MyEclipse 自带的TomCat 新增部署的时候不显示 Deploy Location
项目总是报错,添了删,删了又添了N次以后,发现添加部署的时候,Deploy Location 没有值了,Deploy Location 没有值在自带的Tomcat上就无法用浏览器浏览(Open in ...