Rails 用 Rails 折腾微信 API 的一点小技巧

edokeh · 2013年02月27日 · 最后由 miserytan 回复于 2017年02月13日 · 16760 次阅读

之前用 Rails 弄过微信 API,无比轻松,几十行代码就能搭建一个简单的接口 不过要折腾得大一点的时候,发现代码组织上会比较麻烦,比如接口要实现这样的功能:如果发送 @+ 字符串,返回消息 A,如果发送图片,返回消息 B,发送其他文本,返回消息 C,这时候代码可能会这样

def create
    query_type = params[:xml][:MsgType]
    if query_type == "image"
        do_method_b
    elsif query_type == "text"
        query = params[:xml][:Content]
        if query.start_with? "@"
            do_method_a
        else
            do_method_c
        end
    end
end

可以看到大量的逻辑堆在一个方法里面,即便用子方法切开,看起来也很乱。如果能够像 route 一样,根据请求的不同,由不同的 Controller 来处理的话,代码会清晰很多。我翻了下 route 的 API,发现还真能这样做,constraints 参数可以根据 request 的不同来决定路由

scope :path => "/weixin", :via => :post do
  root :to => 'weixin#method_a', :constraints => lambda { |request| request.params[:xml][:MsgType] == 'image' }

  root :to => 'weixin#method_b', :constraints => lambda { |request| request.params[:xml][:MsgType] == 'text' && request.params[:xml][:Content].start_with?('@') }

  root :to => 'weixin#method_c', :constraints => lambda { |request| request.params[:xml][:MsgType] == 'text' }
end

这样看上去就清晰多了,如果嫌后面的 lambda 还是太繁琐,可以用一个 class 再稍微封装一下,比如我现在封装后的代码是这样

scope :path => "/weixin", :via => :post do
  root :to => 'weixin#method_a', :constraints => Weixin::Router.new(:type => "image")

  root :to => 'weixin#method_b', :constraints => Weixin::Router.new(:type => "text", :content => /^@/)

  root :to => 'weixin#method_c', :constraints => Weixin::Router.new(:type => "text")
end

共同折腾微信 API 的同志们可以参考下

好经验分享,顶之!

才刚刚吐槽过微信竟然用 XML

#2 楼 @HungYuHei 唉,是啊,XML 太繁琐,而且微信 API 目前也只是个半残品

微信现在有语音的接口吗?

#4 楼 @tarzansos 还没有开放呢

1,完整的 code 有么,我看你在 github 上的没有新的路由的那部分。对

Weixin::Router.new(:type => "image")

这一段没懂哈;

2,rails 4 似乎有说如下,是不是说 4 开始要加 gem 才能这么轻松处理接收的 xml

Remove support for parsing XML parameters from request. If you still want to parse XML parameters, please install `actionpack-xml_parser' gem.

3,对于不同的请求 type,可以在逻辑代码里面用 send 不同的方法来区分,和通过路由来区分各有啥优劣

class DemoWeixin::Router
  def initialize(type="text")
    @message_type = type
  end

  def matches?(request)
    xml_data = request.params[:xml]
    if xml_data and xml_data.is_a?(Hash)
      @message_type == request.params[:xml][:MsgType]
    end
  end
end

DemoWeixin::Application.routes.draw do

  get "welcome/index"

  get "message/io"   => "message#auth"
  #post "message/io"  => "message#talk"
  scope "/", via: :post do
    #match "message/io" => "message#reply_text", constraints: lambda {|request| request.params[:xml].nil? }
    #match "message/io" => "message#reply_image", constraints: lambda {|request| request.params[:xml] && request.params[:xml][:MsgType] == "text"}
    match "message/io" => "message#reply_text", constraints: DemoWeixin::Router.new("text")
    match "message/io" => "message#reply_image", constraints: DemoWeixin::Router.new("image")
    match "message/io" => "message#reply_location", constraints: DemoWeixin::Router.new("location")
    match "message/io" => "message#reply_link", constraints: DemoWeixin::Router.new("link")
    match "message/io" => "message#reply_event", constraints: DemoWeixin::Router.new("event")
    match "message/io" => "message#reply_music", constraints: DemoWeixin::Router.new("music")
    match "message/io" => "message#reply_news", constraints: DemoWeixin::Router.new("news")
    match "message/io" => "message#reply_news", constraints: lambda {|r| r.params}
  end

  root to: 'welcome#index'
end

https://github.com/as181920/demo_weixin

回复字数有限制,但是不知道具体是多少

#6 楼 @as181920 哦,那个我写在另外一个项目里面,可以看看这里https://github.com/edokeh/bajiao-weixin/blob/master/config/initializers/weixin_router.rb 这样做的好处嘛,一是可维护性更高,router 中的声明式代码肯定比普通命令式的代码更容易理解和修改 另外一点,如果用子方法的话,其实每个子方法并不是 Rails 的 action 方法,这样使用上会有些局限,比如不能为其中的某个子方法单独加 filter Rails4 不支持 XML 的 POST 了啊?我还没注意到,去看看先

严重怀疑微信的内部 api 肯定有更好的文档,可能可以用 json 传数据了

#10 楼 @futer 我猜内部肯定有隐藏的机制,比如招行的微信账号,居然能一次回复两条

相比 node, 有啥优势?

没啥,如果会 ruby 开发成本低吧,如果会 node 那就 node 成本低吧,暂时这些也没有什么性能和安全的问题。

匿名 #14 · 2013年03月14日

学习了…………

@edokeh 请教一下楼主,像例如我用招行信用卡做完一比交易,然后招行的公众号可以直接推一条交易内容的信息给我,请问这个推的接口是怎么调用的...微信文档里面找不到啊...

#15 楼 @reducm 是的,主动推送不在官方接口里面,招行跟微信有内部合作,所有有更高级的接口

#15 楼 @reducm #16 楼 @edokeh

现在的服务号交钱就有一个客服接口了.

在用户推送消息给你之后, 你可以在 24 小时还是 12 小时内, 对他不断推信息.

我是用元编程处理这些代码的杂乱问题,写了有文章在这里: http://ruxpin.is-programmer.com/posts/39118.html

看了一晚上,总算搞明白了 Rails route constraints

我资质太差了。

请教楼主一个问题,为什么使用 root to,而不是

post  ':username',  :to => 'weixin#method_a', :constraints => lambda { |request| request.params[:xml][:MsgType] == 'image' }

@xiaoronglv 因为上面有了 :via => :post 呗

24楼 已删除

#23 楼 @ruby_sky 我是用 rails4,加了了 actionpack-xml_parser gem,验证的 URL 填写的是http://example.com/weixin/show 验证通过,但是发消息都是走 POST http://example.com/weixin/showURL 改为,肯定错误,随后我把验证 http://example.com/weixin/create ,通过,但是问题是没 POST ,为什么会有两个 URL 微信不通常都说一个吗?谢谢。

学习了,谢谢

@edokeh 能留一个邮箱或者是联系方式给我吗,有些问题需要讨教一下

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册