リア充爆発日記

You don't even know what ria-ju really is.

RESTful APIとWebサイトを1つのアプリケーションで作る

RailsでWEB APIサーバを作るときのアーキテクチャをどうするか考えるの続き。

routingなど具体的にどうRailsに盛り込んでいくかの巻。

前提

とりあえずテスト

テストしたいのは・・・

  • API側はapi.example.comからのアクセスしか受け付けないようになっていること。
    • 何もしないとアプリは1つなので、example.comからも叩けちゃう。問題切り分けとかのときにややこしくならないように。
  • Jsonフォーマットでレスポンスが返ってくること

user_api_spec.rb

  describe "GET /user" do
    let(:user) { FactoryGirl.create(:user) }

    it "should respond with json" do
      get v1_user_path(user), nil, {'HTTP_HOST' => 'api.example.com'}
      parsed_body = JSON.parse(response.body)
      parsed_body["name"].should == user.name
    end

    it "should not respond access from domains other than the subdomain for api" do
      expect { get v1_user_path(user), nil, {'HTTP_HOST' => 'example.com' } }.to raise_error
    end

  end

実際は、このテストも一発で書けた訳じゃなくて、かなりトライアンドエラーを重ねてるんだけど、とにかくポイントは

get v1_user_path(user), nil, {'HTTP_HOST' => 'api.example.com'}

の部分。

getは3つ目のオプションでHOST指定できるのでここで「サブドメイン宛のみ許可」のテストができる。

エラーパターンとして以下のテストを書いた。

    it "should not respond access from domains other than the subdomain for api" do
      expect { get v1_user_path(user), nil, {'HTTP_HOST' => 'example.com' } }.to raise_error
    end

RoutingErrorになるのでこの形になった。404にするのが正しいのかな、と一瞬思ったけどまぁこれでもいいか、ということにした。

このテストを通すコードが以降。

ルーティング

config/routes.rb

  constraints subdomain: 'api' do
    namespace :v1 do
      resources :users
    end
  end

"constraints subdomain: 'api'"でapi.のサブドメイン宛のみ有効。
"namespace :v1"でapp/controllers/v1ディレクトリ配下のコントローラーを見に行く。

コントローラ

app/controllers/v1/application_controller.rb

class V1::ApplicationController < ActionController::Base
  respond_to :json
  before_filter :set_format

  def set_format
    request.format = :json
  end
end

フォーマットはJsonしか扱わないのでここで決め打っておく。V2ができたときにいろいろ重複しそうだなぁ、と思ったけど、できたときに解決するのでも全然問題ないと思ったので今は気にしないことにした。


app/controllers/v1/users_controller.rb

class V1::UsersController < V1::ApplicationController

  def show
    @user = User.find(params[:id])
    respond_with @user
  end
end

respond_withを毎回書くのはめんどくさい、と思ったけど、省略するやり方がわからなかったのと、Jsonだとhas_manyなmodelの場合、どこまで追っていくか指定したりするからこれでしょうがないのか、と思うことにした。


こんな感じで行こうっと。