はじめに (対象読者・この記事でわかること)
この記事は、Ruby on RailsでAPI開発を行っている開発者、JavaScriptでフロントエンド開発を行っている開発者を対象にしています。特に、SPA(シングルページアプリケーション)やモバイルアプリケーションとRails APIを連携させたいと考えている方に最適です。
この記事を読むことで、Ruby on RailsのAPIモードでクロスオリジン通信を実現するためのCORS設定方法がわかります。具体的な設定手順やコード例を交えて解説するので、実際のプロジェクトにすぐに応用できるようになります。また、実装でよく遭遇するエラーとその解決策についても触れているので、開発の効率化に繋がるでしょう。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。 - Ruby on Railsの基本的な知識 - JavaScriptとAJAX/Fetch APIの基本的な知識 - HTTPリクエストとレスポンスの基本的な理解
クロスオリジン通信とは?なぜ必要なのか
Webアプリケーション開発において、フロントエンドとバックエンドを別々のドメインで運用することは珍しくありません。例えば、ReactやVue.jsで構築したフロントエンドアプリケーションと、Ruby on Railsで構築したAPIサーバーを別々のドメインで運用するケースです。
しかし、セキュリティ上の理由により、ブラウザは同一オリジンポリシー(Same-Origin Policy)というセキュリティ制限を課しています。これは、あるドメインのページから、別のドメインのリソースへアクセスを制限する仕組みです。これにより、悪意のあるサイトがユーザーのデータに不正にアクセスするのを防いでいます。
この制限を回避し、異なるオリジン間で安全に通信を行うための仕組みがクロスオリジンリソース共有(CORS: Cross-Origin Resource Sharing)です。CORSを適切に設定することで、許可されたオリジンからのみAPIへのアクセスを許可することができます。
Ruby on RailsのAPIモードでは、デフォルトでCORSの設定が行われていないため、明示的に設定を行う必要があります。この記事では、Rails APIモードでCORSを設定し、JavaScriptからAPIを呼び出す方法を具体的に解説します。
Rails APIモードでのCORS設定と実装方法
ステップ1:Rails APIモードでのCORS設定
まずは、Rails APIモードでCORSを設定します。最も一般的な方法は、rack-corsというgemを使用することです。
gemの追加
まず、Gemfileにrack-corsを追加します。
Ruby# Gemfile gem 'rack-cors'
次に、ターミナルで以下のコマンドを実行してgemをインストールします。
Bashbundle install
CORSの設定
次に、config/initializers/cors.rbファイルを作成し、CORSの設定を記述します。このファイルはデフォルトでは存在しないため、新規作成する必要があります。
Ruby# config/initializers/cors.rb Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:3000', 'http://localhost:8080' # 許可するオリジンを指定 resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head], credentials: true end end
上記の設定では、http://localhost:3000とhttp://localhost:8080からのアクセスを許可しています。originsには、許可したいフロントエンドのドメインを指定します。開発環境ではlocalhostで動作させるため、ポート番号も指定する必要があります。
resource '*'は、すべてのリソースに対して設定を適用することを意味します。headers: :anyは、すべてのヘッダーを許可することを示しています。methodsには、許可するHTTPメソッドを指定します。credentials: trueは、認証情報(クッキーなど)を含むリクエストを許可する設定です。
本番環境では、許可するオリジンを実際のドメインに変更してください。また、セキュリティ上の理由から、必要なオリジンだけを許可することをお勧めします。
設定の確認
設定が完了したら、Railsサーバーを再起動します。これで、CORSの設定が有効になります。
Bashrails server
ステップ2:フロントエンド側での実装
次に、JavaScriptからRails APIにアクセスする実装例を紹介します。ここでは、Fetch APIを使用した例を示します。
GETリクエストの例
Javascript// APIからデータを取得する例 fetch('http://localhost:3000/api/users', { method: 'GET', credentials: 'include' // 認証情報を含める場合 }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { console.log(data); }) .catch(error => { console.error('There has been a problem with your fetch operation:', error); });
POSTリクエストの例
Javascript// データをAPIに送信する例 fetch('http://localhost:3000/api/users', { method: 'POST', headers: { 'Content-Type': 'application/json', }, credentials: 'include', // 認証情報を含める場合 body: JSON.stringify({ name: 'John Doe', email: 'john@example.com' }) }) .then(response => { if (!response.ok) { throw new Error('Network response was not ok'); } return response.json(); }) .then(data => { console.log(data); }) .catch(error => { console.error('There has been a problem with your fetch operation:', error); });
上記の例では、credentials: 'include'を指定することで、クッキーなどの認証情報をリクエストに含めています。Rails側でcredentials: trueを設定している場合、この設定が必要です。
また、Content-Typeヘッダーをapplication/jsonに設定することで、JSON形式のデータを送信しています。Rails側でparams.require(:user).permit(:name, :email)のようなStrong Parametersを使用している場合、送信するデータのキーと値が一致している必要があります。
ハマった点やエラー解決
CORSの設定を実装する際に、よく遭遇する問題とその解決策をいくつか紹介します。
エラー1:Preflightリクエストが失敗する
現象: OPTIONSメソッドのリクエストに対して、405 Method Not Allowedや403 Forbiddenのエラーが返る。
原因: CORSでは、リクエストにカスタムヘッダーが含まれる場合や、GET/HEAD以外のメソッドを使用する場合、ブラウザはまずOPTIONSメソッドでプリフライトリクエストを送信します。サーバーがこのOPTIONSリクエストに正しく応答しないため、エラーが発生します。
解決策:
config/initializers/cors.rbの設定で、OPTIONSメソッドを許可していることを確認します。
Rubymethods: [:get, :post, :put, :patch, :delete, :options, :head]
また、カスタムヘッダーを使用する場合は、exposeで公開するヘッダーを明示的に指定する必要があります。
Rubyresource '*', headers: :any, expose: ['X-Custom-Header'], # 公開するヘッダーを指定 methods: [:get, :post, :put, :patch, :delete, :options, :head]
エラー2:認証情報(クッキー)が送信されない
現象: ログイン状態の維持に必要なクッキーがリクエストに含まれず、認証が失敗する。
原因:
CORSの設定でcredentialsが有効になっていない場合、ブラウザは認証情報をリクエストに含めません。
解決策:
Rails側のCORS設定でcredentials: trueを指定し、フロントエンド側でcredentials: 'include'を指定します。
Rails側の設定:
Rubyresource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head], credentials: true
フロントエンド側の設定:
Javascriptfetch('http://localhost:3000/api/users', { credentials: 'include' // 認証情報を含める })
エラー3:特定のオリジンからのアクセスが拒否される
現象: 特定のオリジンからのアクセスだけが拒否される。
原因: CORSの設定で、許可するオリジンが正しく指定されていない可能性があります。
解決策:
config/initializers/cors.rbのoriginsで、許可するオリジンが正しく指定されていることを確認します。
Rubyallow do origins 'http://example.com', 'http://localhost:3000' # 許可するオリジンを正しく指定 # ... end
開発環境では、http://localhost:3000やhttp://localhost:8080などのポート番号を含むURLを指定する必要があります。
エラー4:Rails APIモードでCORSの設定が効かない
現象:
rack-corsをインストールし、設定ファイルを作成したにもかかわらず、CORSの設定が効かない。
原因:
Rails APIモードでは、デフォルトでActionDispatch::Staticミドルウェアが有効になっており、静的ファイルの配信時にCORSの設定が適用されない可能性があります。
解決策:
config/application.rbで、APIモードかどうかを判定し、CORSの設定を適用するタイミングを調整します。
Ruby# config/application.rb module YourAppName class Application < Rails::Application # ... config.api_only = true # APIモードの場合のみCORSの設定を適用 if config.api_only config.middleware.insert_before 0, Rack::Cors do allow do origins 'http://localhost:3000', 'http://localhost:8080' resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head], credentials: true end end end end end
このようにすることで、APIモードの場合のみCORSの設定が適用されるようになります。
環境ごとの設定の切り替え
開発環境と本番環境で、CORSの設定を切り替える方法を紹介します。
環境変数を使用する方法
config/initializers/cors.rbで、環境変数を使用して許可するオリジンを切り替えることができます。
Ruby# config/initializers/cors.rb Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do # 環境変数から許可するオリジンを取得 origins ENV['ALLOWED_ORIGINS'].split(',') if ENV['ALLOWED_ORIGINS'] resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head], credentials: true end end
開発環境では、.envファイルに以下のように設定します。
# .env
ALLOWED_ORIGINS=http://localhost:3000,http://localhost:8080
本番環境では、サーバーの環境変数に実際のドメインを設定します。
Rails.envを使用する方法
Rails.envを使用して、環境ごとに設定を切り替えることもできます。
Ruby# config/initializers/cors.rb Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do if Rails.env.development? origins 'http://localhost:3000', 'http://localhost:8080' elsif Rails.env.production? origins 'https://your-production-domain.com' end resource '*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head], credentials: true end end
この方法では、開発環境と本番環境で異なるオリジンを許可することができます。
まとめ
本記事では、Ruby on Rails APIモードでクロスオリジン通信を実現する方法について解説しました。
- CORS設定の重要性: セキュリティを保ちつつ、異なるオリジン間で通信を行うためにはCORSの設定が不可欠です。
- rack-cors gemの使用:
rack-corsgemを使用することで、簡単にCORSの設定を行うことができます。 - 具体的な設定手順:
config/initializers/cors.rbで許可するオリジンやメソッドを指定する方法を学びました。 - フロントエンド側の実装: JavaScriptのFetch APIを使用してAPIにアクセスする方法を確認しました。
- よくあるエラーとその解決策: プリフライトリクエストの問題や認証情報の送信問題など、実装でよく遭遇する問題とその解決策を学びました。
この記事を通して、Rails APIモードとJavaScript間のクロスオリジン通信を安全かつ簡単に実装できるようになったことでしょう。今後は、より複雑なAPIの連携や、パフォーマンス最適化などについても記事にする予定です。
参考資料