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 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
--- :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 }