【轮子狂魔】WeChatAPI 开源系统架构详解
| 如果使用WeChatAPI,它扮演着什么样的角色? |

从图中我们可以看到主要分为3个部分:
1.业务系统
2.WeChatAPI:
WeChatWebAPI,主要是接收微信服务器请求;
WeChatAPI Service:主要是提供一些微信基础操作给业务系统使用。(这里有个问题,目前规划是不包含回调业务系统的。当然这并不是无法扩展的。)
3.微信服务器
| 项目结构 |

1.Test:测试目录
1.1UnitTest:单元测试目录
WeChatService.Test:微信服务测试
2.UI:界面目录
WeChatWebAPI:微信网页接口,接收微信服务器请求
3.Server:服务端目录
Business:主要是通过触发Event来执行Commands,隔离底层业务逻辑。
Commands:真正的微信业务指令,在此层与微信服务器交互。
Contracts:提供微信服务接口契约。
Events:定义微信业务事件。
Models:定义数据契约、微信交互对象、枚举等。
4.Insfrastructure:基础设施目录
4.1Lib:类库目录
CommonLib:通用辅助类库
WCFLib:WCF辅助类库
4.2Plugins:插件目录
DispatchchingCenter:调度中心负责映射Event和Command的关系,并提供一些通用业务逻辑的处理。
5.Publish:发布目录(目前还没有做,这里将会做成Windows服务,主要是真正发布时用的环境)
6.DevEnvironment:开发环境目录(主要是针对开发,方便调试)
DevHost:开发主机,可直接针对此项目进行调试。
| 时序图 |

| 实现一个接口:自定义菜单查询 |
微信API传送门:http://mp.weixin.qq.com/wiki/16/ff9b7b85220e1396ffa16794a9d95adc.html
| 分析微信接口 |
1. http请求:GET,地址为:https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
2.请求参数只有一个 access_token
3.返回参数,这个微信偷懒了,可以看创建菜单接口里很清晰 http://mp.weixin.qq.com/wiki/13/43de8269be54a0a6f64413e4dfa94f39.html
| 创建Model |
根据分析接口得到一个结果,发送请求时是不需要新的实体,而返回结果是需要的。
BaseMenu.cs
/// <summary>
/// 菜单基类
/// </summary>
[DataContract(Name = "menu")]
[KnownType(typeof(ClickMenu))]
[KnownType(typeof(DropDownMenu))]
[KnownType(typeof(ViewMenu))]
public class BaseMenu
{
/// <summary>
/// 菜单名称
/// </summary>
[DataMember(Name = "name")]
public string Name { get; set; }
}
MenuList.cs
/// <summary>
/// 菜单列表
/// </summary>
[DataContract(Name = "menu_list")]
public class MenuList
{
/// <summary>
/// 菜单集合
/// </summary>
[DataMember(Name = "button")]
public List<BaseMenu> MenuSet { get; set; }
}
DropDownMenu.cs
/// <summary>
/// 下拉菜单
/// </summary>
[DataContract(Name = "drop_down_menu")]
public class DropDownMenu : BaseMenu
{
/// <summary>
/// 子菜单
/// </summary>
[DataMember(Name = "sub_button")]
public List<BaseMenu> MenuSet { get; set; }
}
ViewMenu.cs
/// <summary>
/// 跳转URL菜单
/// </summary>
[DataContract(Name = "view_menu")]
public class ViewMenu : BaseMenu
{
/// <summary>
/// 类型
/// </summary>
[DataMember(Name = "type")]
public string Type { get { return "view"; } set { } } /// <summary>
/// 跳转的URL
/// </summary>
[DataMember(Name = "url")]
public string Url { get; set; }
}
ClickMenu.cs
/// <summary>
/// 点击推事件菜单
/// </summary>
[DataContract(Name = "click_menu")]
public class ClickMenu : BaseMenu
{
/// <summary>
/// 类型
/// </summary>
[DataMember(Name = "type")]
public string Type { get { return "click"; } set { } } /// <summary>
/// 事件关键字
/// </summary>
[DataMember(Name = "key")]
public string Key { get; set; }
}
需要注意两点:
1. DataContract(Name=xxx) 和 DataMember(Name=xxx) 其实是告诉序列化器,序列化后的参数名。因为C#的命名规则跟微信的不一样,所以为了方便C#开发,我保持了原有的命名规则,通过这种方式在序列化时改成微信要求的参数名。
2. BaseMenu的[KnowType(typeof(xxx))] ,这个是因为在接口定义上并没有细化到 DorpDownMenu、ViewMenu、ClickMenu,而是抽离出一个基类来操作的,为了引用时可以感知到派生类,需要增加KnowType特性,这是WCF的事情,不详说了。
| 创建Event |
GetMenuEvent.cs
/// <summary>
/// 查询菜单事件
/// </summary>
public class GetMenuEvent : DispatchEvent, IAccessTokenAuth
{
public string AccessToken { get; set; } public MenuList MenuList { get; set; }
}
通过上面分析微信接口我们知道,参数应该只有一个AccessToken,为什么会多一个MenuList?
这个问题之前文章有写到,简单提一下,是为了Commands直接操作引用类型,把值从GetMenuEvent类传回来。
DispatchEvent:这个是告诉Dispatcher,当前类是一个调度事件。这样Dispatcher就会在你激活这个事件时去找对应的Command。
IAccessTokenAuth:调度器执行DispatchHandler之前会执行BeforeActive,从而激活AccessTokenCommand.FillAccessToken来帮助填充AccessToken。这一块之前也有说过。
| 实现Command |
1.添加一个CommandRequest,指定访问的URL
MenuCommandRequest.cs
/// <summary>
/// 查询菜单命令请求
/// </summary>
public class GetMenuCommandRequest : BaseCommandRequest
{
public GetMenuCommandRequest(string accessToken)
{
this.Method = HttpMethod.GET;
this.URL = string.Format("https://api.weixin.qq.com/cgi-bin/menu/get?access_token={0}", accessToken);
}
}
2.添加一个Command,实现GetMenuEvent
/// <summary>
/// 查询菜单
/// </summary>
/// <param name="e"></param>
[DispatchHandler(typeof(GetMenuEvent))]
public void GetMenu(GetMenuEvent e)
{
var commandRequest = new GetMenuCommandRequest(e.AccessToken); e.MenuList = CommandHelper.GetWeChatResponseObject<Models.Menu.MenuList>(commandRequest);
}
| 创建Business |
MenuOperation.cs
public MenuList GetMenu()
{
var getMenuEvent = new GetMenuEvent(); Dispatcher.ActiveEvent(getMenuEvent); return getMenuEvent.MenuList;
}
| 创建Service Contract |
IMenuService.cs
[OperationContract]
[FaultContractAttribute(typeof(FaultMessage))]
MenuList GetMenu();
WeChatService.cs
public Models.Menu.MenuList GetMenu()
{
return new Businesses.Menu.MenuOperation().GetMenu();
}
这个类库关系到你的服务是否真正的提供出去。因为这里使用的是WCF,Copy我的类即可,如果研究WCF的话可自行搜索相关教程。
| 修改配置文件 |
因为我并没有新增一个新的服务契约接口类,所以此时并不需要增加下面的代码,只是点明一下是哪里让它生效的而已。
DevHost项目的app.config
<endpoint address="http://localhost:15000/WeChatService/Menu"
binding="basicHttpBinding"
bindingConfiguration="WeChatBinding"
contract="Contracts.Interfaces.Menu.IMenuService" >
</endpoint>
PS:这个例子流程上没什么大问题,就是json序列化成menu的时候为null,因为赶着出这篇博客,所以还没修复这个问题。
等我有空把这个修复一下。大概定位到问题是我这里抽象了一个BaseMenu,然后序列化器不识别。
| 你是不是跟我们一样,对编程还充满激情? |
我可以做这些事情是因为我对知识的敬畏,越了解越可怕。
但我不畏惧,因为这探索知识的路上有我、有你、有他们。
如果你想加入我们,可以加我们的群:7424099
最后,源码传送门:http://git.oschina.net/doddgu/WeChatAPI
最后的最后,再次提示:这并不是一个完整的项目!!!
PS:时序图里出现PrintSQL,这个其实不存在的,因为Copy了之前的文档,图片已经上传了就没改。
【轮子狂魔】WeChatAPI 开源系统架构详解的更多相关文章
- WeChatAPI 开源系统架构详解
WeChatAPI 开源系统架构详解 如果使用WeChatAPI,它扮演着什么样的角色? 从图中我们可以看到主要分为3个部分: 1.业务系统 2.WeChatAPI: WeChatWebAPI,主要是 ...
- 大型web系统架构详解
(如果感觉有帮助,请帮忙点推荐,添加关注,谢谢!你的支持是我不断更新文章的动力.本博客会逐步推出一系列的关于大型网站架构.分布式应用.设计模式.架构模式等方面的系列文章) 动态应用,是相对于网站静态内 ...
- Flume日志收集系统架构详解--转
2017-09-06 朱洁 大数据和云计算技术 任何一个生产系统在运行过程中都会产生大量的日志,日志往往隐藏了很多有价值的信息.在没有分析方法之前,这些日志存储一段时间后就会被清理.随着技术的发展和 ...
- NopCommerce源码架构详解--初识高性能的开源商城系统cms
很多人都说通过阅读.学习大神们高质量的代码是提高自己技术能力最快的方式之一.我觉得通过阅读NopCommerce的源码,可以从中学习很多企业系统.软件开发的规范和一些新的技术.技巧,可以快速地提高我们 ...
- NopCommerce源码架构详解
NopCommerce源码架构详解--初识高性能的开源商城系统cms 很多人都说通过阅读.学习大神们高质量的代码是提高自己技术能力最快的方式之一.我觉得通过阅读NopCommerce的源码,可以从 ...
- Nop--NopCommerce源码架构详解专题目录
最近在研究外国优秀的ASP.NET mvc电子商务网站系统NopCommerce源码架构.这个系统无论是代码组织结构.思想及分层都值得我们学习.对于没有一定开发经验的人要完全搞懂这个源码还是有一定的难 ...
- Hyperledger Fabric架构详解
区块链开源实现HYPERLEDGER FABRIC架构详解 区块链开源实现HYPERLEDGER FABRIC架构详解 2018年5月26日 陶辉 Comments 10 Comments hyper ...
- mysql show variables系统变量详解
mysql系统变量详解 mysqld服务器维护两种变量.全局变量影响服务器的全局操作.会话变量影响具体客户端连接相关操作. 服务器启动时,将所有全局变量初始化为默认值.可以在选项文件或命令行中指定的选 ...
- 领域驱动设计(Domain Driven Design)参考架构详解
摘要 本文将介绍领域驱动设计(Domain Driven Design)的官方参考架构,该架构分成了Interfaces.Applications和Domain三层以及包含各类基础设施的Infrast ...
随机推荐
- (1)封装 (2)static关键字 (3)继承
1.封装(重中之重)1.1 基本概念 通常情况下,可以在测试类中给成员变量进行赋值,当给定的数值合法但不合理时,无论是编译还是运行阶段都不会报错或给出提示,此时与现实生活不符. 为了避免上述问题的发生 ...
- October 08th 2017 Week 41st Sunday
Talent wins games, but teamwork and intelligence wins championships. 才华让你赢得比赛,团队及智慧让你赢得冠军. But the m ...
- 关于Excel中的数据透视表没有数据
在你想要使用数据透视表的时候,区域一定要正确 然后把你想要的数据按行列排好 如果没有数据 请点击刷新数据……刷新数据……刷新数据 我竟然被这个睿智的问题困扰好久……
- kudu安装部署
安装部署节点规划 节点 kudu-master kudu-tserver node01 是 是 node02 是 是 node03 是 是 配置本地Yum的Repository 下载kudu安装yum ...
- ui-sref
angularjs中路由跳转可以在模板页面上使用ui-sref="a-state({param1: value})"; 如果想为当前state的导航按钮添加一个激活class,可以 ...
- Extjs TreePanel API详解
转自:http://web.qhwins.com/CSS-JS-XML/2011091312092944999107.html config定义{ animate : Boolean, contain ...
- python中pandas里面的dataframe数据的筛选小结
pandas大家用的都很多,像我这种用的不够熟练,也不够多的就只能做做笔记,尽量留下点东西吧. 筛选行: a. 按照列的条件筛选 df = pandas.DataFrame(...) # suppos ...
- 第七周:Python
python的应用场景 重复性的东西编写脚本 和对于大数据量的操作 数据搭建的环境 不建议自己在网上找下载,建议下载anaconda,可在清华镜像里面下载anaconda,下载安装之后可在桌面上找到程 ...
- BZOJ2425:[HAOI2010]计数(数位DP)
Description 你有一组非零数字(不一定唯一),你可以在其中插入任意个0,这样就可以产生无限个数.比如说给定{1,2},那么可以生成数字12,21,102,120,201,210,1002,1 ...
- 【openjudge】【递推】例3.4 昆虫繁殖
[题目描述] 科学家在热带森林中发现了一种特殊的昆虫,这种昆虫的繁殖能力很强.每对成虫过x个月产y对卵,每对卵要过两个月长成成虫.假设每个成虫不死,第一个月只有一对成虫,且卵长成成虫后的第一个月不产卵 ...