RailsにJSONデータをPostする
概要
クライアント側からポストされたJSON形式のデータを、サーバ側でparamsから利用する。
こんな感じ。
クライアント側
$ telnet localhost 3000 POST /projects HTTP/1.1 Accept: application/json Content-Type: application/json Content-Length: * { project : { name : "Hello!", desc : "Hello World Project!" } }
サーバ側
def create
project = Project.new
project.name = params['project']['name']
project.desc = params['project']['desc']
project.save
...
end
通常の場合
通常の場合、クライアント側からポストされるデータは、例えばブラウザのフォームからのデータだった場合、"application/x-www-form-urlencoded"形式である。
HTMLに以下のように書いてあれば、
<form method='post'> <input type='text' name='project[name]' value='Hello!' /> <input type='text' name='project[desc]' value='Hello World Project!' /> ...
送信時にブラウザによって"application/x-www-form-urlencoded"形式のデータに変換されてサーバに送られる。
ActiveResourceの場合
RESTfulなRailsサーバのクライアントとして、ActiveResourceが挙げられる。ActiveResourceクライアントはRailsとの通信にXMLを利用する。それはクライアントからサーバへデータを送信するときも同様である。
例えば上記と同じデータをActiveResouceクライアントは以下のようなフォーマットでサーバに送信する。
<project> <name>Hello!</name> <desc>Hello World Project!</desc> </project>
サーバ側からは、データ形式が"application/x-www-form-urlencoded"であった場合と同じように、paramsメソッドを利用してこのデータを取得できる。
def create
project = Project.new
project.name = params['project']['name']
project.desc = params['project']['desc']
project.save
...
end
サーバ側の挙動
Raisサーバ側はクライアントからポストされたデータが"application/x-www-form-urlencoded"であるか"application/xml"であるかをクライアント側のContent-Typeを識別して、それぞれのデータをハッシュに変換する。コントローラからはこのハッシュ化されたデータをparamsメソッドを利用して取得することができる。
このクライアント側からのデータをハッシュに変換するパーサはActionController::Base.param_parsersに登録されている。
例えば"application/xml"に対応するparserは、以下の書式で取得することができる。
ActionController::Base.param_parsers[Mime::Type::lookup("application/xml")]
逆に言えばparam_parsersにparserが登録されているデータ形式ならば何でも、サーバ側でparamsメソッドからハッシュとして利用できると言うことである。
JSONデータのparserを登録する
ActionController::Base.param_parsersに登録されているparserは、Raw Dataを受け取り、ハッシュに変換されたデータを返すProcオブジェクトである。
従って、JSON形式、content-typeが"application/json"であるRaw Dataをハッシュに変換するparserを登録するには、config/environment.rbに以下のように設定してやればよい。
ActionController::Base.param_parsers[Mime::Type::lookup('application/json')] = Proc.new { |data|
ActiveSupport::JSON.decode(data)
}
なお、ActiveSupport::JSON.decodeメソッドはRails 1.2.3をインストールしたときについてくるActiveSupportにはまだ無いAPIなので、最新のActiveSupportをレポジトリから取得しておく必要がある。
Jakarta Commons HttpClientを利用してはまったこと
上記の設定をすれば、JSONオブジェクトをRailsサーバにポストできるはずだが、HttpClientを利用して以下のコードでデータをポストしてみたところ、見事にはまった。
Project project = ...;
PostMethod post = new PostMethod("http://localhost:3000/projects.json");
StringRequestEntity entity = new StringRequestEntity(project.toJSON().toString(), "application/json", "utf-8");
post.setRequestEntity(entity);
this.getHttpClient().executeMethod(post);
原因は、StringRequestEntityを生成するときにcontent-typeとcharsetを指定するのだが、これをそのままPostすると、Content-Type="application/json; charset=utf-8"というヘッダが生成されてしまうからだった。
べつに良いじゃんと思わないでもないが、Railsに登録されているのは"application/json"であって、"application/json; charset=utf-8"で無かったため、異なるMime-Typeであると判断されてしまった模様。
結局、
StringRequestEntity entity = new StringRequestEntity(project.toJSON().toString(), "application/json", null);
とし、charsetは別ヘッダーとすることで解決した。