GrapeとDoorkeeperでRailsアプリに認証付きのREST-like Web APIを実装する 前編
Railsチュートリアルの第2版を参考にして作ったマイクロブログに、GrapeとDoorkeeperを使用して、OAuth認可を利用するWeb APIを実装しようと試みました。
前編では、Grapeを使ったAPIの実装までを記載しています。
Doorkeeperを使ったOAuth認可機能の実装は、後編に記載します。
目次
補足:実装したいAPI v1の仕様
- マイクロブログにチャットボットからマイクロポストをPOSTしたい
- APIを利用するアプリの作成や認可はサインインしているユーザのみ可能にしたい
- つまるところTwitter APIの機能を縮小したようなAPIを作りたい
APIの実装を試みるマイクロブログは、Railsチュートリアルの第2版を参考にして作ってますので、User
モデルとMicropost
モデルが関連して成り立っています。
認証システムも、認証システムを提供するgem
を使用せずにRailsのsession
メソッドを使用して構築していますので、session
メソッドを使用する認証メソッドをSessionsHelperに定義して、色々な場所で認証メソッドを使えるようになっています。
GrapeとDooreeperのGemを追加
REST-like APIフレームワークのGrapeと、OAuth 2プロパイダ機能を導入できるDoorkeeperのgemをGemfile
に追加*1し、bundle install
します。
gem 'grape', '0.16.2' gem 'doorkeeper', '4.0.0'
GrapeでAPIを実装
RailsアプリにGrapeでAPIを実装する場合の作法に習ってapp/api
ディレクトリを作成し、APIを構成するファイルを配置します。
一般的に、APIはバージョンごとにパスを分ける方が良いらしいので、API v1
の機能を記述したv1_api.rb
を配置するために、v1
ディレクトリも作成します。
/railsapp
|- /app
| |- /api
| | |- api.rb
| | |- /v1
| | |- v1_api.rb
config/application.rb
Railsがapp/api
配下のファイルやディレクトリを読み込むように、config
にパスの自動読込を記述します。
require File.expand_path('../boot', __FILE__) ・ ・ ・ class Application < Rails::Application ・ ・ ・ # Auto-load API and its subdirectories config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb') config.autoload_paths += Dir[Rails.root.join('app', 'api', '*')] end end
app/api/api.rb
app/api
の直下にAPIの基底クラスを記述したapi.rb
を作成し、API v1
の機能が記述されているモジュールV1::V1API
をmount
します。
module API class Base < Grape::API prefix 'api' version 'v1', using: :path format :json mount V1::V1API end end
app/api/v1/v1_api.rb
API v1
では、v1_api.rb
でresource
に指定したシンボルstatuses
*2がAPIのroot URLになるため、API v1
のroot URLはhttp://railsapp.com/api/v1/statuses
になるようです。
APIを実装するマイクロブログでは、user_id
とcontent
のPOST
を受け取ってマイクロポストを作成するため、post
ブロックで必要なパラメータを受け取り、Micropost
をcreate
できるようにします。
また、params
ブロックで必要なパラメータを記述しておくと、パラメータの誤りや不足があった場合に{"error":"user_id is invalid, content is missing"}%
とか教えてくれるようです。
実際に利用する予定のpost
の他に、動作確認のためのget
ブロックも作成しています。
module V1 class V1API < Grape::API resource :statuses do desc 'Get the root url' get '/' do status 200 end desc 'Get 3 Microposts' get '/index' do Micropost.limit(3) end desc 'Post new micropost' params do requires :user_id, type: Integer requires :content, type: String end post do Micropost.create!({ user_id: params[:user_id], content: params[:content] }) status 201 end end end end
ルーティングにAPIをMount
config/routes.rb
に、APIの基底クラスBase
を含むモジュールAPI::Base
をmount
しAPIのルートを設定します。
Rails.application.routes.draw do mount API::Base => '/' ・ ・ ・
API v1の動作確認
rails server
を起動して、curl
コマンドでAPI v1
の動作を確認します。
$rails server => Booting WEBrick => Rails 4.2.6 application starting in development on http://localhost:3000 => Run `rails server -h` for more startup options => Ctrl-C to shutdown server [2016-06-14 21:57:52] INFO WEBrick 1.3.1 [2016-06-14 21:57:52] INFO ruby 2.3.0 (2015-12-25) [x86_64-darwin15] [2016-06-14 21:57:52] INFO WEBrick::HTTPServer#start: pid=62210 port=3000
Get the root url
まずは、root URLにアクセスしてみます。
$curl http://localhost:3000/api/v1/statuses/ 200%
すんなりと、ステータスコード200
が表示されました。
rails server
のログも、問題無さそうです。
Started GET "/api/v1/statuses/" for ::1 at 2016-06-14 21:59:24 +0900
Get 3 microposts
次に、/index
にアクセスして、limit(3)
のマイクロポストを取得できるか確認してみます。
$curl http://localhost:3000/api/v1/statuses/index [{"id":300,"content":"Nesciunt quia numquam vel pariatur earum corrupti.","user_id":6,"created_at":"2016-06-09T12:47:11.903Z","updated_at":"2016-06-09T12:47:11.903Z"},{"id":299,"content":"Nesciunt quia numquam vel pariatur earum corrupti.","user_id":5,"created_at":"2016-06-09T12:47:11.900Z","updated_at":"2016-06-09T12:47:11.900Z"},{"id":298,"content":"Nesciunt quia numquam vel pariatur earum corrupti.","user_id":4,"created_at":"2016-06-09T12:47:11.898Z","updated_at":"2016-06-09T12:47:11.898Z"}]%
Faker gemで生成した、愉快なマイクロポストが表示されました。
rails server
のログでも、SQLが実行されている様子が確認できます。
Started GET "/api/v1/statuses/index" for ::1 at 2016-06-14 22:04:17 +0900 Micropost Load (0.6ms) SELECT "microposts".* FROM "microposts" ORDER BY created_at DESC LIMIT 3
Post new micropost
それでは、API v1
を実装した目的であるマイクロポストのPOST
機能を試してみます。
$curl -d "user_id=2&content=test" http://localhost:3000/api/v1/statuses 201%
- curl … 指定したURLへデータの送受信を行うコマンド
- -d "name=value" … データをPOSTリクエストとして送信できるオプションで'&'を使って複数項目をまとめて送れる
user_id
が2
のユーザのマイクロポストに、test
の文字をPOST
してみました。
rails server
のログでは、SQLのINSERSTが確認できます。
Started POST "/api/v1/statuses" for ::1 at 2016-06-14 22:22:12 +0900 (0.8ms) BEGIN SQL (28.7ms) INSERT INTO "microposts" ("user_id", "content", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["user_id", 2], ["content", "test"], ["created_at", "2016-06-14 13:22:12.080868"], ["updated_at", "2016-06-14 13:22:12.080868"]] (27.5ms) COMMIT
念のため、データベースも確認してみます。
$psql railsapp_development Expanded display is used automatically. Null display is "NULL". psql (9.5.1) Type "help" for help. railsapp_development=# SELECT * FROM microposts ORDER BY id DESC LIMIT 1; id | content | user_id | created_at | updated_at -----+----------+---------+----------------------------+---------------------------- 301 | api test | 2 | 2016-06-14 13:22:12.080868 | 2016-06-14 13:22:12.080868 (1 row) railsapp_development=#
ちゃんと保存されていましたヽ(´д`)ノ
参考記事
Grapeに関しては、こちらの記事を参考にさせていただきました。
- GitHub - ruby-grape/grape: An opinionated framework for creating REST-like APIs in Ruby.
- Rails x Grapeで簡単API開発 - プログラミングノート
- RailsとGrapeをはじめてつかってみた | Webuilder240
- Grape での mass assignment 対策 - Qiita
Doorkeeperに関しては、こちらの記事を参考にさせていただきました。
- GitHub - doorkeeper-gem/doorkeeper: Doorkeeper is an OAuth 2 provider for Rails
- Rails 4.1 で Doorkeeper を使った OAuth2 Provider のサンプルを実装した - @znz blog
- Rails で API サーバーの認証の仕組みを作る - nirasan's tech blog
- よくわかる認証と認可 | Developers.IO
cURLに関しては、こちらの記事を参考にさせていただきました。
開発環境
- OSX 10.11.5 El Capitan
- テキストエディタ: MacVim
- ターミナルエミュレータ: Macターミナル
- シェル: zsh
- パッケージマネージャ: Homebrew - Ruby 2.3.0
- バージョンマネージャ: rbenv
- Webフレームワーク: Ruby on Rails 4.2.6 - データベース
- ORDBMS: PostgreSQL 9.5.1