production.log

ピクスタ株式会社で開発部の部長をやっている星直史のブログです。

Railsでpreflightリクエストを処理し、no route matches optionsを回避する方法

概要

前回の記事ではRailsを使って、APIの受け側を作りました。
ただ、このままだとJSでフロントエンドからAPIに対してリクエストした場合、preflightリクエストが飛ぶので、No route matches [OPTIONS]エラーが返ってしまい、正常終了できません。
今回は、Railsでpreflightリクエストを処理し、no route matches optionsを回避する方法を書きます。

処理の方針

fetch("http://localhost:3000/auth_user", {
  method: 'POST',
  body:  JSON.stringify({ emai: "emai@email.com", password: "********"})
}).then(function(response) {
  return response.json();
}).then(function(json) {
  ...
});

こんな感じでフロントからリクエストをすることを想定します。
fetchでリクエストする場合、クロスドメインアクセスが可能か確認するするため、OPTIONSリクエストを飛ばして、それが200になってから本来のリクエストを飛ばします。
また、preflightリクエストが成功にするには、2つ条件があります。

  • OPTIONSをルーティングできること
  • response.headersに適切なキーとバリューが設定されていること

今回は、上記2つの条件を満たすための処理を書くことを目標にします。

OPTIONSをルーティングできること

乱暴ではありますが、いかなるpathのリクエストに対してもOPTIONSリクエストであれば、response.headersの設定を行えるようにします。 config/routes.rbに下記を追加しましょう。

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

今回はoptions_request_controller.rbを作成して、そこで処理をすることにします。

response.headersに適切なキーとバリューが設定されていること

ルーティングファイルに従って、options_request_controller.rbを作成します。 また、response.headersに適切なキーとバリューが設定しなければならないので、このように処理をします。

class OptionsRequestController < ApplicationController
  ACCESS_CONTROL_ALLOW_METHODS = %w[GET OPTIONS PUT DELETE POST].freeze
  ACCESS_CONTROL_ALLOW_HEADERS = %w[Accept Origin Content-Type Authorization].freeze
  ACCESS_CONTROL_MAX_AGE = 86_400

  protect_from_forgery except: :preflight

  before_action :set_preflight_headers!, only: [:preflight]

  def preflight
    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(',')
    response.headers['Access-Control-Allow-Origin'] = '*'
    head :ok
  end
end

こちらの処理の通り、response.headersの中にいくつかキーとバリューを設定しています。
レスポンスヘッダーの詳細説明に関しては、こちらの記事に譲るとして、やっていることをざっくり説明するとこのようになります

キー バリュー 意味
Access-Control-Max-Age 86400 preflightリクエストの結果を24時間キャッシュ
Access-Control-Allow-Headers Accept,Origin,Content-Type,Authorization リクエスト時にどのヘッダーが使用可能か明示
Access-Control-Allow-Methods GET,OPTIONS,PUT,DELETE,POST リソースのアクセス時に許容するメソッドを明示
Access-Control-Allow-Origin * 許容できるアクセス元のURLを明示

まとめ

今回、とりあえずハマりを抜け出すために、簡易な処理を書きましたが、要点としては下記2点です。

  • OPTIONSリクエストを受け付けるようにする
  • 受け付けたOPTIONSリクエストのレスポンスにAccess-Control-Allow-ほげほげをつける

SPAでAPI連携をするときにハマりポイントではありますし、何回作っても毎度ハマるので参考にしていただければと思います