respond_to vs routes 指定の format
イントロ
例えばAPI サーバーを作るときに「この URL にアクセスする時は、常に json
でアクセスしてほしい」みたいな事を実現したい。
それを実現する具体的な方法としては namespace :api, format: 'json'
と routes を書けばいいと思ってました。
(デフォルトの format が json になるので)
ただ
- それ以外の format を渡した時にどう動くか
- 似たような事ができる実装とどう違うのか
がよくわかってなかったので、その時にした実験をもう一度やってみたメモ。
パターン1
class Api::AccountsController < ApplicationController def index @accounts = Account.all respond_to do |format| format.html { render } format.json { render } end end end
Rails.application.routes.draw do namespace :api, format: 'json' do resources :accounts end end
- controller 側では
respond_to
で複数 format を受けつける準備ができている - namespace では
json
を指定
結果
% curl -I http://localhost:3000/api/accounts HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: application/json; charset=utf-8 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: 01d34bba-60c5-4185-9e96-e343d179c45b X-Runtime: 0.014599 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 10 Connection: Keep-Alive
% curl -I http://localhost:3000/api/accounts.html HTTP/1.1 404 Not Found Content-Type: text/html; charset=utf-8 Content-Length: 41119 X-Web-Console-Session-Id: 939cd9fb2834ea8353d1332a62d97e15 X-Request-Id: a77ea27e-f5c0-4766-b357-09b964188e55 X-Runtime: 6.597595 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Connection: Keep-Alive
- controller で受け取る
format
はjson
になる - response の
Content-Type
もapplication/json
json
以外の format の request が投げられたら、404 Not found
- controller 側では html をの時は html の render をする準備ができているのにも関わらず
- routes 側で 404 になってしまうので、controller に到達しない
- controller 側に到達した時には、常に
json
形式- (書いてしまってるけど) 実質
renspond_to
で複数 format 対応は 不要
- (書いてしまってるけど) 実質
パターン2
class Api::AccountsController < ApplicationController def index @accounts = Account.all respond_to do |format| format.html { render } format.json { render } end end end
Rails.application.routes.draw do namespace :api do resources :accounts end end
- format 指定を削除
- controller 側は変わらず
結果
% curl -I http://localhost:3000/api/accounts HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 # text/html に注目! Cache-Control: max-age=0, private, must-revalidate X-Request-Id: 4db51f09-53cc-42ca-a6a0-49e4080b5030 X-Runtime: 0.492789 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 1715 Connection: Keep-Alive
% curl -I http://localhost:3000/api/accounts.html HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: text/html; charset=utf-8 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: 7d446561-2768-4f20-8832-4d6476115d45 X-Runtime: 0.050431 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 1715 Connection: Keep-Alive
json
が通るか確かめる
% curl -I -X GET http://localhost:3000/api/accounts.json HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: application/json; charset=utf-8 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: c3c4a0fb-847c-4240-9470-606695c6b935 X-Runtime: 0.023534 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 10 Connection: Keep-Alive
respond_to
での指定以外のformat
% curl -I http://localhost:3000/api/accounts.xml HTTP/1.1 406 Not Acceptable Content-Type: text/html; charset=utf-8 Content-Length: 115323 X-Web-Console-Session-Id: bfb96347bd3ab4058a468ebb5762595f X-Request-Id: 74ee3306-aa22-4615-a72d-c1a7602ef434 X-Runtime: 0.640820 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Connection: Keep-Alive
respond_to do ~ end
で指定する format の内、 最初に書いてある方が default になるrespond_to
ブロックで指定している format 以外の request が投げられたら406 Not acceptable
responders
class Api::AccountsController < ApplicationController respond_to :json def index @accounts = Account.all respond_with @account end end
Rails.application.routes.draw do namespace :api do resources :accounts end end
結果
% curl -I http://localhost:3000/api/accounts HTTP/1.1 200 OK X-Frame-Options: SAMEORIGIN X-Xss-Protection: 1; mode=block X-Content-Type-Options: nosniff Content-Type: application/json; charset=utf-8 Cache-Control: max-age=0, private, must-revalidate X-Request-Id: f7a06375-320d-4d37-a93c-12d9d3c17cf0 X-Runtime: 0.299781 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Content-Length: 0 Connection: Keep-Alive
% curl -I http://localhost:3000/api/accounts.html HTTP/1.1 406 Not Acceptable Content-Type: text/html; charset=utf-8 Content-Length: 115331 X-Web-Console-Session-Id: 780f1e5e4050cb0b7e72d36f60841f4c X-Request-Id: b0f92ee8-c6fa-4dc4-872e-f60adc942104 X-Runtime: 0.379881 Server: WEBrick/1.3.1 (Ruby/2.2.3/2015-08-18) Connection: Keep-Alive
- パターン2 とほぼおなじ
respond_to
も複数個 format が渡せるが、format
指定無しでリクエストされた場合、最初に指定されてある format が利用されるrespond_to :html, :json
なら、html
がデフォルト値
- 指定以外の
format
でのリクエストは、406 Not acceptable
responders
の利点は、controller 毎に format の指定をしなくて済むことなのかな?- 一箇所にまとめられていれば、controller 毎にデフォルトの
format
が異なるという間違いを避けられる。 - (一瞬触っただけなので、よくわかってない)
- 一箇所にまとめられていれば、controller 毎にデフォルトの
結論
サーバーが指定外の format
で request を受けた時、どう返したいかで実装が変わる
406 Not acceptable
を返す場合はrespond_to
(あるいはresponders
)- 複数 format を受け付ける URL なら、こちらのほうが自然
404 Not Found
を返す場合はnamespace :prefix, format: 'format名'
- 文字列 で指定すること。(
symbol
じゃダメ) - 例えば「
json
しか絶対受け付けない」なら、こういうのもありかも。
- 文字列 で指定すること。(