酒と泪とRubyとRailsと

Ruby on Rails と Objective-C は酒の肴です!

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モードとして生成されます。

1
$ rails new my_api --api

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

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

既存のアプリをAPI Modeにする場合

configを以下のように修正。

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

各controller を以下の様に変更。

1
2
3
4
5
class ApplicationController < ActionController::Base
end
# ↓
class ApplicationController < ActionController::API
end

APIモードのmiddleware

以下のコマンドを実行するとrailsのmiddlewareをみる事ができます。

1
$ rails middleware

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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?を使うだけです。

1
2
3
4
5
6
7
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 メソッドを利用することで実際にファイルを送付することができる。 このメソッドを使う場合は以下の様な設定を行う必要がある。

1
2
3
4
5
6
# 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 の活用

1
2
3
4
5
6
7
8
jQuery.ajax({
  type: 'POST',
  url: '/people',
  dataType: 'json',
  contentType: 'application/json',
  data: JSON.stringify({ person: { firstName: "Yehuda", lastName: "Katz" } }),
  success: function(json) { }
});

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

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

middlewareの追加・削除

1
2
3
4
5
# middlewareの追加
config.middleware.use Rack::MethodOverride

# middlewareの削除
config.middleware.delete ::Rack::Sendfile

(補足) HTTP ETag

「HTTP ETag」はキャッシュの有効性を検証するための仕組みの一つ。

Special Thanks

おすすめの書籍