JSON 形式とかで非同期メッセージ連携

以前からある機能ですが、備忘をかねてエントリ。
デフォルトの設定では、AP4R を経由して非同期メッセージを (メッセージを処理する) サーバに投げると、

  • HTTP POST メソッド
  • HTTP header 部に Content-Type: application/x-www-form-urlencoded
  • HTTP body 部に urlencoded 形式な文字列


てな感じにしています。
で、Rails の場合、ActionController::Base.param_parsers に登録されている parser が受けとった文字列を Hash に変換して渡してくれます。アクションのなかで、params[:hoge] で受けとってるやつです。


これをもう少しだけいろんな形式で投げられるようにしてみました。



たとえば、JSON 形式。

非同期メッセージの送信元である Rails で、以下のようににコントローを書きます。

  def hogehoge

    # do something

    ap4r.async_to( {:controller => 'async_world', :action => 'execute_via_http'} ) do
      body :key1, "hoge"                                                                                            
      body :key2, 1
      body :key3, { :a => "b", :c => "d"}
      format :json
    end

  end


すると、

  • (HTTP POST メソッド)
  • Content-Type: application/json
  • HTTP body 部: {"key3": {"c": "d", "a": "b"}, "key1": "hoge", "key2": 1}

となります。
ちなみにデフォルトの設定だと、以下のような urlencoded 形式な文字列でした。

  • HTTP body 部: key3[c]=d&key3[a]=b&key1=hoge&key2=1

さて、試しに JSON 形式で投げたリクエストを受けとってみましょう。
activesupport-2.0.x からは、JSON 形式の decode 処理が含まれているので、environment.rb に以下のように追記すると、MIME type 別の処理ロジックを追加することができます。

ActionController::Base.param_parsers[Mime::Type::lookup('application/json')] = Proc.new { |data|
  ActiveSupport::JSON.decode(data)
}

実際、さきほどのメッセージを投げると、script/server したコンソールで params による解釈ができているのが分かります。

Processing AsyncWorldController#execute_via_http (for 127.0.0.1 at 2008-01-25 18:37:25) [POST]
Session ID: 0eaa1feecb21f7957b90f6f334c76707
Parameters: {"key1"=>"hoge", "key2"=>"1", "key3"=>{"a"=>"b", "c"=>"d"}, "action"=>"execute_via_http", "controller"=\
>"async_world"}


同様に、:json としていた部分を :xml にすれば xml 形式で、:text とすれば メッセージを to_s した形式で送ることになります。yaml 形式にも対応しました。
すべて ActiveSupport のおかげですが...w


それぞれ以下のとおりです。

  • :xml の場合
  • Content-Type: application/xml
  • HTTP body 部: 整形すると...↓
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <key3>
    <c>d</c>
    <a>b</a>
  </key3>
  <key1>hoge</key1>
  <key2 type="integer">1</key2>
</root>
  • :text の場合
  • Content-Type: text/plain
  • HTTP body 部: key3cdabkey1hogekey21
  • :yaml の場合
  • Content-Type: text/yaml
  • HTTP body 部: ↓
---
:key3:
  :c: d
  :a: b
:key1: hoge
:key2: 1

なお、Rails でこれらの形式を受けとる場合、:xml と :text は parser が解釈してくれますが:yaml の場合は :json のときと同じように environment.rb に追記します。あ、2.0 でしか試してないです。

ActionController::Base.param_parsers[Mime::Type::lookup('text/yaml')] = Proc.new { |data|
  YAML.load(data)
}


蛇足ですが、
なぜか strategy としてはきってあるのに、@param_parsers に含まれてないのです...?



action_controller/request.rb L.376 付近

      def parse_formatted_request_parameters
        ...
        case strategy
          when Proc
            strategy.call(body)
          when :url_encoded_form
            self.class.clean_up_ajax_request_body! body
            self.class.parse_query_parameters(body)
          when :multipart_form
            self.class.parse_multipart_form_parameters(body, boundary, content_length, env)
          when :xml_simple, :xml_node
            body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
          when :yaml
            YAML.load(body)
          else
            {}
        end

action_controller/base.rb L.318 付近

    @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
                        Mime::URL_ENCODED_FORM => :url_encoded_form,
                        Mime::XML => :xml_simple }