Edited at

Rails APIでクロスドメインとpre-flight対応

More than 1 year has passed since last update.

localでRails APIとWebクライアントを別途立てていた時に、クロスドメインとpre-flightへの対応が必要になりました。

Rails APIサーバ: localhost:3000

Webフロントエンド: localhost:3355

Webフロントエンドでは、HTTPクライアントとしてaxiosを利用しました。


クロスドメイン対応

クロスドメイン対応が無い場合、リクエストで"No 'Access-Control-Allow-Origin' header is present on the requested resource." とエラーが出ます



スクリーンショット 2017-03-06 17.46.37.png

localhost:3355からのアクセスに対して、クロスドメイン対応をいれます。


config/application.rb

config.action_dispatch.default_headers = {

'Access-Control-Allow-Credentials' => 'true',
'Access-Control-Allow-Origin' => 'http://localhost:3355',
'Access-Control-Request-Method' => '*'
}

未検証ですが、下記gemではrackレベルで対応可能のようです。

cyu/rack-cors


参考

Rails4アプリに対してクロスドメインでAjaxでCookieを送信したい場合


pre-flight対応

axiosは異なるオリジンに対してリクエストする時に、OPTIONSメソッドでpre-flightのリクエストを送信します。

How to disable OPTIONS request?

OPTIONSメソッドでリクエストしようとして404になります

スクリーンショット 2017-03-06 17.57.59.png

Railsサーバは以下のようなエラーが出ます

Started OPTIONS "/user_token" for ::1 at 2017-03-06 17:57:46 +0900

ActiveRecord::SchemaMigration Load (3.9ms) SELECT `schema_migrations`.* FROM `schema_migrations`

ActionController::RoutingError (No route matches [OPTIONS] "/user_token"):

Railsでのpreflightリクエストの実装およびテスト例の実装を参考に実装してみます。

routes.rbの先頭でOPTIONSメソッドでのリクエストに対して、options_requests_controllerをルーティングします。


config/routes.rb

Rails.application.routes.draw do

match '*path' => 'options_request#preflight', via: :options
# other resources
end

ヘッダに'Access-Control-Max-Age', 'Access-Control-Allow-Headers', 'Access-Control-Allow-Methods' をつけて返します。


app/controllers/options_request_controller.rb

class OptionsRequestController < ApplicationController

ACCESS_CONTROL_ALLOW_METHODS = %w(GET OPTIONS).freeze
ACCESS_CONTROL_ALLOW_HEADERS = %w(Accept Origin Content-Type Authorization).freeze

def preflight
set_preflight_headers!
head :ok
end

private

def set_preflight_headers!
response.headers['Access-Control-Max-Age'] = ACCESS_CONTROL_MAX_AGE
response.headers['Access-Control-Allow-Headers'] = ACCESS_CONTROL_ALLOW_HEADERS.join(',')
response.headers['Access-Control-Allow-Methods'] = ACCESS_CONTROL_ALLOW_METHODS.join(',')
end
end