Rails 5.0.0.beta2 APIモードについて調べてみた

Using Rails for API-only Applications — Ruby on Rails Guides」を中心に、Rails 5.0.0.beta2 API modeについて少し調べてみたのでそのメモです。


😸 APIアプリケーションのおさらい

まず、ここではAPIアプリケーションを「GitHub Developer | GitHub Developer Guide」のように、プログラムがアクセスすることを前提としたWebアプリケーションと考えいます。

クライアントサイドにNativeアプリケーションがあり、バックエンドにRailsのアプリケーションがJSONのリソースを扱うことを想定しています。

🚌 なぜJSON APIにRailsを使うのか?

Railsが提供しているCoCのベストプラクティスを有効活用して、ビジネスロジックをスピーディかつ柔軟にAPIを構築できる点が、
RailsをAPIアプリケーションとして活用するメリットだと考えます。

😀 Railsを使うメリット

- リクエストが来るたびに、変更内容を監視して最適なリロードが実行される(開発者のプロダクティビティを落とさない)
- 開発モードを持ち、開発者のプロダクティビティを向上させる(本番モードのパフォーマンスは落とさない)
- 環境情報やDBのクエリー、基本的なパフォーマンスなどのログを出力する
- セキュリティがしっかりしている(IPスプーフィングや、Timing Attack等を防いでくれる)
- URLパラメータだけでなく、JSONパラメータ等の解釈できる
- 変更がない場合に304 Not Modifiedを返すConditional GETを使う事ができる(`#stale?`, ETag, Last-Modified)
- Railsの強力なキャッシュ機能(page, action, fragment caching)を有効活用することができる
- RESTful JSON APIを構築するのであればResourceful Routingは最適
- Basic認証、Digest認証、Token認証等の基本的な認証方法をサポートしている
- シングルコマンドで、controller, model, routes, test stub等々を生成できる強力なジェネレータを持つ
- 沢山のサードパーティのライブラリが、RailsによるWebアプリの構築をサポートしてくれる

🚕 新規のアプリケーションを構築する場合

次のように --api オプションをつけるとAPIモードとして生成されます。

$ rails new my_api --api

このコマンドによって生成されるRailsアプリケーションには次のような特徴があります。

- 通常のWebアプリよりも、Middlewareが少ないので応答性能が向上する
- ApplicationController => ActionController::APIを継承する
- GeneratorでのViewやAssetの生成が行われない(フロントエンド側が行うことを想定)

🎂 既存のアプリケーションをAPI Modeにする場合

configを次のように修正。

# config/application.rb
config.api_only = true
# config/environments/development.rb
config.debug_exception_response_format = :api

各controllerを以下のように変更。

class ApplicationController < ActionController::Base
end
# ↓
class ApplicationController < ActionController::API
end

🍣 APIモードのmiddleware

次のコマンドを実行するとrailsのmiddlewareをみることができます。

$ rails middleware

ちなみに、APIモードのmiddlewareは次のとおりです。

Rack::Sendfile #=> X-Sendfile header をセットする
ActionDispatch::Static #=> Publicフォルダの静的なファイルを返す
ActionDispatch::LoadInterlock #=> 開発モードでThread safeなコードリロードを行う
ActiveSupport::Cache::Strategy::LocalCache::Middleware #=> メモリキャッシュを行う
Rack::Runtime #=> X-Runtime header をセットする
ActionDispatch::RequestId #=> リクエストごとにユニークな X-Request-Id header をセットする
Rails::Rack::Logger #=> リクエストの開始と終了にログを書き込む
ActionDispatch::ShowExceptions #=> アプリで例外が発生した時にユーザーに例外を返す
ActionDispatch::DebugExceptions #=> localでDegug用例外ページを返す
ActionDispatch::RemoteIp #=> IP spoofing attacksのチェックを行う
ActionDispatch::Reloader #=> 開発中のコードのリロードを行う
ActionDispatch::Callbacks #=> リクエスト前とリクエスト後のコールバックを行う
Rack::Head #=> HEAD リクエストを GETリクエストに変換する
Rack::ConditionalGet #=> 変更がない場合にConditional GET(304 Not Modified)を返す
Rack::ETag #=> ETag header を追加する

🐠 Cache Middlewareの活用

HTTP Cacheの基本的な使い方は、controllerで #stale?を使うだけです。

def show
@post = Post.find(params[:id])
if stale?(last_modified: @post.updated_at)
render json: @post
end
end

この方法は、If-Modified-Sinceヘッダに @post.updated_at をセットします。
最後の更新から変更がない場合は、「304 Not Modified」を返します。

🍄 Rack::Sendfileの活用

Rack::Sendfile は、controllerで #send_file メソッドを利用することで実際にファイルを送付できる。
このメソッドを使う場合は以下のような設定を行う必要がある。

# config/environemts/production.rb
# Apache and lighttpd
config.action_dispatch.x_sendfile_header = "X-Sendfile"
# Nginx
config.action_dispatch.x_sendfile_header = "X-Accel-Redirect"

🏀 ActionDispatch::Requestの活用

jQuery.ajax({
type: 'POST',
url: '/people',
dataType: 'json',
contentType: 'application/json',
data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }),
success: function(json) { }
});

もControllerで次のように正しく解釈される。

{ :person => { :firstName => "Yehuda", :lastName => "Katz" } }

🏈 middlewareの追加・削除

# middlewareの追加
config.middleware.use Rack::MethodOverride
# middlewareの削除
config.middleware.delete ::Rack::Sendfile

🐹 (補足) HTTP ETag

「HTTP ETag」はキャッシュの有効性を検証するためのしくみのひとつ。

😎 参考リンク

📚 おすすめの書籍