pockestrap

Programmer's memo

rails-apiでcookieを使う

最近、rails-apicookieを使うような機会が何度かあったので、その方法をメモしておきます。

tl;dr

rails-api with cookie · pocke/rails-api-with-cookie-sample@11ccaf1

前置き

rails-apiのデフォルトでは、cookieが無効化されています。
そのため、セッション管理などでcookieを有効にするにはひと手間必要です。

試してみる

では、実際に試してみましょう。 以下のコマンドでrails-apiの新規プロジェクトを開始します。

$ rails-api new rails-api-with-cookie-sample

例として、cookieを使用して訪問回数を返すAPIを作成します。

# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  def root
    t = (cookies['count'] || 1).to_i
    cookies['count'] = t + 1
    render text: "#{t} 回目の訪問です!\n"
  end
end
# config/routes.rb
Rails.application.routes.draw do
  root 'application#root'
end

上記を一旦保存し、bin/rails sでサーバーを起動します。
そして、APIらしくcurlhttp://localhost:3000を叩いてみると、500エラーが返ってきてしまいます。

$ curl localhost:3000 -c cookie.txt -b cookie.txt
# Rails のエラーページのHTMLが返ってくる…
Started GET "/" for ::1 at 2016-06-23 19:06:33 +0900
Processing by ApplicationController#root as */*
Completed 500 Internal Server Error in 3ms (ActiveRecord: 0.0ms)

NameError (undefined local variable or method `cookies' for #<ApplicationController:0x00000003c7d398>):
  app/controllers/application_controller.rb:3:in `root'


  Rendered /home/pocke/.gem/ruby/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_source.erb (1.8ms)
  Rendered /home/pocke/.gem/ruby/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (1.3ms)
  Rendered /home/pocke/.gem/ruby/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (0.5ms)
  Rendered /home/pocke/.gem/ruby/2.3.0/gems/actionpack-4.2.6/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (10.6ms)

どうやらcookiesメソッドが存在しないようですね。

cookiesメソッドを読み込んでみる

このメソッドはActionController::Cookiesで定義されているようなので、このモジュールをincludeしてみましょう。

# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
  include ActionController::Cookies # ココを追記!

  def root
    t = (cookies['count'] || 1).to_i
    cookies['count'] = t + 1
    render text: "#{t} 回目の訪問です!\n"
  end
end
$ curl localhost:3000 -c cookie.txt -b cookie.txt
1 回目の訪問です!
$ curl localhost:3000 -c cookie.txt -b cookie.txt
1 回目の訪問です!
$ curl localhost:3000 -c cookie.txt -b cookie.txt
1 回目の訪問です!
$ curl localhost:3000 -c cookie.txt -b cookie.txt
1 回目の訪問です!
$ curl localhost:3000 -c cookie.txt -b cookie.txt
1 回目の訪問です!

今度は500エラーは出なくなりましたが、何度アクセスしても1 回目の訪問です!と言われてしまいますね。
また、cookie.txtも作成されていないようです。

Rack middleware を読み込んでみる

cookieが有効になっていないのは、CookieをサポートするRack middlewareが読み込まれていないのが原因です。
config/application.rbで対象のmiddlewareを読み込みましょう。

# config/application.rb
module RailsApiWithCookieSample
  class Application < Rails::Application

    # ......

    config.middleware.use ActionDispatch::Cookies
  end
end

そして、railsサーバーを立ち上げ直してからもう一度curlで叩いてみます。

$ curl localhost:3000 -c cookie.txt -b cookie.txt
1 回目の訪問です!
$ curl localhost:3000 -c cookie.txt -b cookie.txt
2 回目の訪問です!
$ curl localhost:3000 -c cookie.txt -b cookie.txt
3 回目の訪問です!
$ curl localhost:3000 -c cookie.txt -b cookie.txt
4 回目の訪問です!
$ curl localhost:3000 -c cookie.txt -b cookie.txt
5 回目の訪問です!

無事カウントアップされましたね!

結論

まとめると、rails-apicookieを同時に使うには以下の2ステップが必要です。

  • ApplicationControllerActionController::Cookiesをinclude
  • Rack middleware として ActionDispatch::Cookies を読み込み

別解

また、ActionDispatch::Cookiesを読み込む代わりに、config.api_only = falseconfig/application.rbに記述する方法もあります。
上記を実施するとActionDispatch::Cookiesが読み込まれる+セッションが有効になる効果があります。

もしセッションを使用するアプリケーションを作る場合は、api_onlyfalseにする方法を取ったほうがよいでしょう。

See: rails-api/default_rails_four_middleware_stack.rb

参考リンク

今回作成したサンプルアプリケーション

参考にしたページなど