微信开发实战笔记
前言
继Rails之后,为何Node火了,这跟移动互联网有和关系? 想要成功,得审时度势,凡事多问几个为毛。最近,我觉得web开发 存在这样几点变化,移动,响应式,单页面应用程序,API,Node。API开发的需求是由移动互联网带动的,Node适合API的开发。海量的 数据,分布式数据库,NoSQL,云端。
平台割据和诸侯混战已结束,互联网进去个性化时代,依附大平台的生存和创新将是今后的主旋律。所以,我也决定依附想微信这样的 平台。
微信平台简介
特点: 私密性和交流
功能: 群发消息, 自定义回复规则,开发模式(分析消息)
使用类似: 企业移动门户,新闻咨询,娱乐,社交,游戏
订阅号和服务号区别,订阅为个人,服务为公司
自动回复和关键字自动回复。公众号的强大之处: 提供的接口。
云计算,IaaS和PaaS,SAE和BAE属于应用服务器引擎,与IaaS相比,灵活性和可配性不强。要用还是用云服务器比较好。
此外,如果应用为PHP环境的话,使用SAE和BAE都挺不错的,PHP果然还是web开发第一语言,可惜我是学Ruby的。
当前实现
设想,使用weixin_rails_middleware, 从而替换现有的实现。不过,对现有 实现进行总结一下,是个不错的注意。
微信的接口是放置在API项目中的,Api项目中总共存在三个东西:ios的接口,android的接口,以及微信的接口。其中,ios的接口和android的接口 内容完全一样,这么大的重复,我个人是难以忍受的,但前辈说,是为了预备以后的修改和不同,没让我改。
接口这东西,就是一个URL,接受写参数,并返回某些响应(XML亦或是JSON数据)。将三个不同的接口,其实就是三组不同的URL。URL是通过Rails的 路由分发的,所以,我看到了这样的做法:
▾ config/
▾ routes/
android_route.rb
ios_route.rb
wx_route.rb
routes.rb
这里,运用了Ruby开放类的特征,routes/
目录下的三个文件,都是对routes.rb
中的类的打开补充。ios和android是以命名空间的方式来分隔URL,微信的接口
和常规的rails路由稍微有些不同,具体的内容如下:
TestApi::Application.routes.draw do
get "/api/winxin/cert" => "weixin/auths#auth"
scope "/", via: :post do
match "api/weixin/cert" => "weixin/auths#info_meals", constraints: WxRouter.new("event", event: "CLICK", event_key: "info_meals")
match "api/weixin/cert" => "weixin/users#subscribe_tags", constraints: WxRouter.new("text", content: /^tag/i)
match "api/weixin/cert" => "weixin/auths#subscribe", constraints: WxRouter.new("event", event: "subscribe")
match "api/weixin/cert" => "weixin/auths#user_scan", constraints: WxRouter.new("event", event: "SCAN", event_key: /\d+/)
match "api/weixin/cert" => "weixin/auths#reply_any", constraints: lambda {|r| r.params} # 接受参代码参数
end
end
# WxRouter类定义, 用来分析和解析类型
class WxRouter
def initialize(msg_type="text", options={})
@message_type = msg_type
@event_key = options[:event_key] if options[:event_key]
@wx_event = options[:event] if options[:event]
@content = options[:content] if options[:content]
end
def matches?(request)
xml_data = request.params[:xml]
if xml_data && xml_data.is_a?(Hash)
(return false unless @message_type == request.params[:xml][:MsgType]) if @message_type
(return false unless @event_key == request.params[:xml][:EventKey]) if @event_key && @event_key.is_a?(String)
(return false unless @event_key =~ request.params[:xml][:EventKey]) if @event_key && @event_key.is_a?(Regexp)
(return false unless @wx_event == request.params[:xml][:Event]) if @wx_event
(return false unless @content == request.params[:xml][:Content]) if @content && @content.is_a?(String)
(return false unless @content =~ request.params[:xml][:Content]) if @content && @content.is_a?(Regexp)
end
true
end
end
仔细阅读和理解这段路由和代码,可以看到,包含了如下的信息:
- 就开发微信接口而言,微信服务器是本地服务器的客户端,get和post请求都是来自微信服务器。
- 微信服务器首先通过get验证授权,然后通过post请求,转发用户的消息,且其消息体为XML
- 消息类型存在: 事件(如菜单点击)和文本
自定义菜单
微信的菜单为 3 × 5的列表,菜单存在多种类型,常见的为click和view(点击的URL之类),最新的微信版本又支持一些 更加高级的菜单。现有实现为如下的类:
class WxMenu
def self.build
menu = {
button: [{
name: "xxxx",
sub_button: [{
type: "click",
name: "yyyy",
key: "info_hot"
},{
type: "click",
name: "zzzz",
key: "info_meals"
}]
}] #button
}
response = JSON.parse RestClient.post((Settings.wx_create_menu_url % WxAccessToken.token), menu.to_json, content_type: "application/json")
STDOUT.puts response.inspect
end
def self.clear
response = JSON.parse RestClient.get(Settings.wx_delete_menu_url % WxAccessToken.token)
STDOUT.puts response.inspect
end
end
# 访问token的实现
class WxAccessToken
def self.token is_force=false
Rails.cache.fetch "wx_token", expires_in: 6000, force: is_force, race_condition_ttl: 10, raw: true do
# 发现access_token每次都会不一样,每次访问,都会使得之前的失效
JSON.parse(RestClient.get "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=#{Settings.wx_appid}&secret=#{Settings.wx_secret}")["access_token"]
end
end
end
这里,该类实现了微信的自定菜单接口的创建和删除,Settings.wx_create_menu_url
为"https://api.weixin.qq.com/cgi-bin/menu/create?access_token=%s"
,
Settings.wx_delete_menu_url
为"https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=%s"
。post请求是通过RestClient
类实现的。
注: 由于个人对接口的理解不到位,导致对如何在测试号中使用自定义菜单的接口不知所措。看了前辈的做法之后,知道,原来是需要自己手动调用方法发post请求过去的啊。
访问token的问题,访问token是每次生成均不相同,没
本地测试
如何测试,是一个很重要的问题,如何利用本地环境测试,是一个极其严肃的问题。
微信官方提供的测试帐号地址: http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login, 本地开发配合ngrok,监听特定的端口,生成http://xxx.ngrok.com
的URL。
意外的发现,ngrok生成的随机的URL其实是固定的,也就说对外的接口层是固定的,本地的Rails开发环境和本地的各种项目来回切换,无缝切换。一个字: “真爽”。
利用测试的appID和appsecret,验证URL以及Token,就可以进行本地测试了,这是一个相当不错的测试方案。
问题: 微信的xxx.xml.erb中,数据模型不能使用ActiveRecord原生的对象,仅支持Ruby的数组对象,所以,需要将ActiveRecord的对象转换成数组对象。此外,微信对消息的格式具有严格的要求, 解析不通过,就说无服务。
大事件: Ngrok被强了,成为又一个GFW的牺牲者。然后,我的微信菜单开发的任务就算到头了,可以正大光明的去干其他事情了。
weixin_rails_middleware
以下,是利用weixin_rails_middleware
进行相关的探索。
环境准备
从Github上,下载其Rails 3的示例项目,修改Rails的版本,删除Gemfile.lock, 添加.ruby-gemset
和.ruby-version
,实现安装环境隔离,bundle。
备注: 意外的发现Ruby的bundle程序极耗CPU,尤其是在解析包关系依赖时,某CPU一直在正弦曲线(振幅为20% - 100%), 还有,在安装某gem包时,CPU轨迹图非常的杂乱无章。
好不容易配置好了,结果,在配置上appid和token上,有点不知所措,权限验证的URL地址,以及文档中的描述也不太明朗,搞的很不愉快。算了,留个标签,以后再说:
参考文献
傲娇的使用Disqus