weixin_rails_middleware 基于 Rails 10分钟跑起一个微信应用

1758 查看

WeixinRailsMiddleware,顾名思义,是供 Rails 使用的微信中间件。帮助你方便地在 Rails 应用中添加微信功能。

安装: bundle intall

安装最新的版本

gem 'weixin_rails_middleware'

或者安装master最新的代码库更新,注意:可能不稳定

gem 'weixin_rails_middleware', git: "https://github.com/lanrion/weixin_rails_middleware.git"

执行 rails generate weixin_rails_middleware:install, 会生成:

  • config/initializers/weixin_rails_middleware.rb
  • app/decorators/controllers/weixin_rails_middleware/weixin_controller_decorator.rb

适用场合

第一种场合:支持多个公众账号,类似微盟的情况:

前往 config/initializers/weixin_rails_middleware.rb

config.public_account_class = "PublicAccount"

注意 PublicAccount 是你保存公众账号的Model名字。

查看在线例子:https://github.com/lanrion/weixin_rails_middleware_example/blob/master/config/initializers/weixin_rails_middleware.rb#L10

如果使用了第一种场合,则需要跑如下命令:

rails generate weixin_rails_middleware:migration public_account_model_name

例子:rails generate weixin_rails_middleware:migration PublicAccount

会生成如下的Migration:

class AddWeixinSecretKeyAndWeixinTokenToPublicAccounts < ActiveRecord::Migration
  def self.up
    change_table(:public_accounts) do |t|
      t.string :weixin_secret_key
      t.string :weixin_token
    end
    add_index :public_accounts, :weixin_secret_key
    add_index :public_accounts, :weixin_token
  end

  def self.down
    # By default, we don't want to make any assumption about how to roll back a migration when your
    # model already existed. Please edit below which fields you would like to remove in this migration.
    raise ActiveRecord::IrreversibleMigration
  end
end

然后执行 rake db:migrate,生成两个字段到数据库。

另外一方面,同时会向你的 app/models/public_accounts.rb 添加一行代码:

include WeixinRailsMiddleware::AutoGenerateWeixinTokenSecretKey

这行代码的作用在于自动生成 weixin_secret_keyweixin_token 两个字段的值,建议不要让用户去填写这两个字段的值,让程序自动生成,如果你想自己定制weixin_secret_keyweixin_token 两个字段的值,删除include WeixinRailsMiddleware::AutoGenerateWeixinTokenSecretKey 这行代码即可。

查看在线例子:https://github.com/lanrion/weixin_rails_middleware_example/blob/master/db/migrate/20140324070325_add_weixin_secret_key_and_weixin_token_to_users.rb

第二种场合:只支持个人公众账号,即不需要把公众账号保存到数据库中:

前往 config/initializers/weixin_rails_middleware.rb

config.weixin_token_string  = 'c06a2a40256fdeb47ff0c7cc'
config.weixin_secret_string = 'J92Eba24yRpG-s9LGYOA03FcnULHYFYs'
  • weixin_token_string: 微信Token值;

  • weixin_secret_string: 微信服务URL配置中,此值用来避免真正使用的Token值暴露在URL上,提高安全性。

默认情况下,在生成 weixin_rails_middleware.rb时,会使用SecureRandom自动生成 weixin_token_stringweixin_secret_string 的值

所以上述的例子,会生成如下的方案:

URL:http://2e13a461.ngrok.com/weixin/J92Eba24yRpG-s9LGYOA03FcnULHYFYs
Token:c06a2a40256fdeb47ff0c7cc

路由

  • 生成微信服务器URL
weixin_engine.weixin_server_url(public_account.weixin_secret_key)
  • 举个生成微信服务URL与Token的例子:

@public_account 是你保存的其中一个公众账号的实例:

网站的地址为: http://2e13a461.ngrok.com

weixin_secret_key 的值为 J92Eba24yRpG-s9LGYOA03FcnULHYFYs

weixin_token 的值为 c06a2a40256fdeb47ff0c7cc

则在app/views/public_accounts/show.html.erb中:

URL:<%= weixin_engine.weixin_server_url(@public_account.weixin_secret_key) %>
Token:<%= @public_account.weixin_token %>

则会生成:

URL:http://2e13a461.ngrok.com/weixin/J92Eba24yRpG-s9LGYOA03FcnULHYFYs
Token:c06a2a40256fdeb47ff0c7cc

即可复制使用到微信服务器配置中。

查看在线例子:https://github.com/lanrion/weixin_rails_middleware_example/blob/master/app/views/users/index.html.erb#L16

配置注意事项

注意: 第一种场合和第二种场合,只能任选一种,如果两种同时配置,会默认使用第二种场合。

业务逻辑实现

前往

app/decorators/controllers/weixin_rails_middleware/weixin_controller_decorator.rb

即为:

# encoding: utf-8
# 1: get weixin xml params
# @weixin_message
# 2: public_account_class instance if you setup, otherwise return nil
# @weixin_public_account
WeixinRailsMiddleware::WeixinController.class_eval do
  before_filter :set_keyword, only: :reply

  def reply
    render xml: send("response_#{@weixin_message.MsgType}_message", {})
  end

  private

    def response_text_message(options={})
      reply_text_message("Your Message: #{@keyword}")
    end

    # <Location_X>23.134521</Location_X>
    # <Location_Y>113.358803</Location_Y>
    # <Scale>20</Scale>
    # <Label><![CDATA[位置信息]]></Label>
    def response_location_message(options={})
      @lx    = @weixin_message.Location_X
      @ly    = @weixin_message.Location_Y
      @scale = @weixin_message.Scale
      @label = @weixin_message.Label
      reply_text_message("Your Location: #{@lx}, #{@ly}, #{@scale}, #{@label}")
    end

    # <PicUrl><![CDATA[this is a url]]></PicUrl>
    # <MediaId><![CDATA[media_id]]></MediaId>
    def response_image_message(options={})
      @pic_url  = @weixin_message.PicUrl
      @media_id = @weixin_message.MediaId # 可以调用多媒体文件下载接口拉取数据。
      reply_text_message("回复图片信息")
    end

    # <Title><![CDATA[公众平台官网链接]]></Title>
    # <Description><![CDATA[公众平台官网链接]]></Description>
    # <Url><![CDATA[url]]></Url>
    def response_link_message(options={})
      @title = @weixin_message.Title
      @desc  = @weixin_message.Description
      @url   = @weixin_message.Url
      reply_text_message("回复链接信息")
    end

    def response_event_message(options={})
      event_type = @weixin_message.Event
      case event_type
      when "subscribe"   # 关注公众账号
        if @keyword.present?
          # 扫描带参数二维码事件: 1. 用户未关注时,进行关注后的事件推送
          return reply_text_message("扫描带参数二维码事件: 1. 用户未关注时,进行关注后的事件推送, keyword: #{@keyword}")
        end
        reply_text_message("关注公众账号")
      when "unsubscribe" # 取消关注
        reply_text_message("取消关注")
      when "SCAN"        # 扫描带参数二维码事件: 2用户已关注时的事件推送
        reply_text_message("扫描带参数二维码事件: 2用户已关注时的事件推送, keyword: #{@keyword}")
      when "LOCATION"    # 上报地理位置事件
        @lat = @weixin_message.Latitude
        @lgt = @weixin_message.Longitude
        @precision = @weixin_message.Precision
        reply_text_message("Your Location: #{@lat}, #{@lgt}, #{@precision}")
      when "CLICK"       # 点击菜单拉取消息时的事件推送
        reply_text_message("你点击了: #{@keyword}")
      when "VIEW"        # 点击菜单跳转链接时的事件推送
        reply_text_message("你点击了: #{@keyword}")
      else
        reply_text_message("处理无法识别的事件")
      end

    end

    # <MediaId><![CDATA[media_id]]></MediaId>
    # <Format><![CDATA[Format]]></Format>
    def response_voice_message(options={})
      @media_id = @weixin_message.MediaId # 可以调用多媒体文件下载接口拉取数据。
      @format   = @weixin_message.Format
      reply_text_message("回复语音信息: #{@keyword}")
    end

    # <MediaId><![CDATA[media_id]]></MediaId>
    # <ThumbMediaId><![CDATA[thumb_media_id]]></ThumbMediaId>
    def response_video_message(options={})
      @media_id = @weixin_message.MediaId # 可以调用多媒体文件下载接口拉取数据。
      # 视频消息缩略图的媒体id,可以调用多媒体文件下载接口拉取数据。
      @thumb_media_id = @weixin_message.ThumbMediaId
      reply_text_message("回复视频信息")
    end

    def set_keyword
      @keyword = @weixin_message.Content    || # 文本消息
                 @weixin_message.EventKey   || # 事件推送
                 @weixin_message.Recognition # 接收语音识别结果
    end

    # http://apidock.com/rails/ActionController/Base/default_url_options
    def default_url_options(options={})
      { weichat_id: @weixin_message.FromUserName }
    end
end

或者猛击, 最新以下面链接内容为主: https://github.com/lanrion/weixin_rails_middleware/blob/master/lib/generators/templates/weixin_controller.rb
里面已经做好了微信所有信息类型的逻辑判断,请再结合微信开发文档来使用。

项目主页

https://github.com/lanrion/weixin_rails_middleware

作者介绍

作者 lanrion,目前专注于云计算中,Paas 平台 Cloud Foundry,分布式架构,数据库。少量业余时间会尝试做一些产品。

喜欢钻牛角尖的家伙,喜欢研究乱七八糟的东西。有任何有关 Ruby and Rails、微信开发、PHP、Java、Nodejs的问题,都可以发邮件一起探讨。微信号:dht_ruby


编辑 SegmentFault