1. 微信公众平台接口用的是xml,因此仿照grape给的示例直接改动format为xml
class MyAPI < Grape::API
version 'v1', :using => :path
format :xml
但回复报错,后来仔细查看文档,需要增加content_type说明
Your API can declare which types to support by using content_type. Response format is determined by the request's extension, an explicit format parameter in the query string, or Accept header.
加上content_type说明后,现在可以接受xml post数据了
class MyAPI < Grape::API
version 'v1', :using => :path
format :xml
content_type :xml, "text/xml"
2. 一开始的时候可以使用params["xml"]["xxx"]可以直接获取解析后的xml post数据,但是后来不知道是哪个gem update了还是怎么回事,传上来的xml post数据不能直接映射到params hash里面,解析出现了问题。这一点很奇怪,只好抛弃params手动解析
body = Hash.from_xml(request.body.read)
3. 以上两条都处理完以后以为可以正常回复了,但是手机上发消息死活还是没有回应。我猜测是不是返回的xml格式问题,我采用的是nokogiri拼装xml,会自动带上xml declaration<? xml version='1.0'?>,原来在做其他平台的xml api解析的时候碰到过xml declaration影响解析的情况。所以就想方设法在输出中去掉了xml declaration,但是没效果,仍然发消息没回复。
google搜索的结果有说道接口回复不能超过5秒,否则有可能视为会话超时(http://www.html-js.com/?p=1527),我是采用nginx+passenger的server,在nginx初次启动passenger的时候确实会比较慢。为了调查是否超时,只能抓包看了。
由于微信接口测试只能在线上环境进行,没法使用wireshark,只能采用tcpdump抓包分析
sudo tcpdump -A -s0 host xxx and port 80 -w ~/dump.cap
抓包结果是回复还是很及时的,基本不超过1秒钟(只是简单的将用户发来的消息回传回去,未作额外处理)。因此应该不是超时的问题。
4. 似乎走到了绝境了,万般无奈之下只能反复的看抓包结果,无意中发现grape post回复的http status code不是一般的200 OK,而是201 created。会不会是微信平台那边不接受非200 status code而拒绝服务?抱着试试看的想法试着加了一行status设置
desc "reply"
post do
body = Hash.from_xml(request.body.read)
status("200")
builder = Nokogiri::XML::Builder.new do |x|
...
end
end
终于,手机端看到回复了,当场泪奔啊...
事后分析grape源码,发现post的时候是默认回复201的
def status(status = nil)
if status
@status = status
else
return @status if @status
case request.request_method.to_s.upcase
when 'POST'
201
else
200
end
end
end
再查看W3C对http post的说明(http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.5)
The action performed by the POST method might not result in a resource that can be identified by a URI. In this case, either 200 (OK) or 204 (No Content) is the appropriate response status, depending on whether or not the response includes an entity that describes the result.
If a resource has been created on the origin server, the response SHOULD be 201 (Created) and contain an entity which describes the status of the request and refers to the new resource, and a Location header
在不创建可以被URI定位的资源的时候,应该回复200或者204,在创建了URI资源的时候,应该回复201。
仔细想想,微信发消息虽然是post,但是也确实也没有创建资源,因此要求status code为200也没错。
可是微信的文档应该说一下啊!!微信公众平台的狗屁文档(http://mp.weixin.qq.com/cgi-bin/readtemplate?t=wxm-callbackapi-doc&lang=zh_CN)这些细节提都没提。折腾了我好几天!!
最后总结一下:
1. 微信的公众平台接口很不完善:
1)只支持80端口,自定义端口还不支持,我实在是无法理解这个有什么技术难度。
2)get方法进行接口校验基本是个摆设,传给一堆参数,还假模假样的设置了一个token进行校验,其实我不判断校验直接返回echostr也一点事儿都没有。而在真正使用post方法传消息的时候却没有给echostr进行校验,只传了以下参数
signature=7f82a86b5f4de67df1d0242793a098a13a3fcffa×tamp=1358324694&nonce=1358209407
这样在post的时候反而没法校验请求是否合法。难以理解为什么要这样设计。
3)文档很不完善,像“5秒超时”和“http status code = 200”这些细节一点都没提,只有开发人员自己去摸索。真是写给经理看的开发文档。
2. grape api框架写的很完善,但是有的地方框的太死了,文档也说得不是很清楚,对json说的比较多,而xml部分基本没提。对于接口调试没有方便的界面来查错,最后只好抓包调试,代价有点高。
3. 杀鸡还是焉用牛刀,对于这么小的需求,其实一个controller/action就能搞定的事情折腾出这么多事儿来。太相信框架/组件也不是什么好事...