くま's Tech系Blog

基本的には技術で学んだことを書き留めようと思います。雑談もやるかもね!

RailsをAPIモードで使用する

今回はRailsAPIモードにしてフロントエンドとバックエンドを分ける方法についてまとめます。

Railsの環境構築については下記記事を参照してください。

kumaskun.hatenablog.com

今回はAPIモードで環境構築された状態から進めていきたいと思います。

また、今回はフロントはVue.jsで進めます。 Vue.js以外の他のフロントエンド環境でも同じように進められると思いますので参考にしてください(Railsを変えることはないはず)

処理の大まかな流れは以下の流れです

フロントからバックエンドへ処理を依頼→バックエンドで処理を行う→結果をJson形式でフロントエンドに渡す

ここで処理を依頼したり、結果を受け取る際にaxiosというライブラリを使用します。

axiosについて

axiosとは、簡単にHTTPリクエストのやり取りができるPromiseベースのライブラリです。 非同期にHTTPメソッドを取り扱うことができます。 今回は、バックエンドとの非同期な通信に利用します。

まずはリクエストを行うフロントエンドでの処理についてです。

使い方は公式がわかりやすく記載されています。

github.com

下記のように使用します(一部分を抜粋しています)。

<script>
import axios from 'axios'

export default {
    methods: {
        async getUser() {
            try {
                const res = await axios.get(import.meta.env.VITE_APP_API_BASE + '/users')

                 console.log({ res });
            } catch (error) {
                console.log({ error })
            }
        }
    }
}
</script>

やっていることは次のような処理です。

  • import fromでaxiosモジュールをインポートする
  • 取得したデータを格納するための変数を定義する
  • axiosの処理を記述する

axiosの処理とは今回の例のようにaxios.get("URL")でAPIの処理をバックエンドに依頼することです。

今回の例ではgetでしたが、postやputなども可能です。また、パラメータを付与するなども可能です。

そして、実行結果はresで受け取れるようになっています。

さらに場合に応じてcatchでエラー処理を行なっています。

バックエンドの処理

次にバックエンドで処理を行い、フロントエンドに結果を渡すまでの流れです。

ここからはRailsで開発する際の基本的な流れと変わりはないです。

Modelの作成

まずはModelを作成します。今回はUserというModelを作成します。

rails g model User id:integer name:string

今回はUserデータをフロントエンドに渡します。 そして、マイグレートを行います。

rails db:migrate

ここでデータは入って入る状態とします。 必要に応じて仮データを入れるなどしてください。

Controllerの作成

次にコントローラーを作成します。

rails g controller Users

ルーティング

ここでルーティングを行います。

ルーティングはフロントエンドでAPIの処理を依頼したときに指定したURLと一致する必要があります。

config/routes.rbでルーティングの設定を行います。

Rails.application.routes.draw do
  resources :users
end

rake routesコマンドで確認してルーティングが設定できているか確認しましょう。

Controllerで処理を行う

最後に先ほど作成したコントローラーで処理を行います。

取得したデータはrender json: オブジェクトとすると、JSON形式のデータを返却することができます。

class UsersController < ApplicationController
      def index
        users = User.all
        render json: { status: 200, message: "API Success", data: users }
      end
end

実行すると下記のようなレスポンスになり、フロントエンドで値を受け取れると思います。(dataというレスポンス)

{
    "status":"SUCCESS",
    "message":"Get User Success!",
    "data":{"id":1,"name":"TEST"}
}

クロスオリジンの設定

Railsの処理をここまで記載しましたが、これだけではレスポンスを取得することはできません。

CORS(クロスオリジン)の設定が必要になります。

Webブウラウザから送信されてくるリクエストが正当なものかどうかをチェックするための仕組みが存在しています。同一生成元ポリシー(Same-Origin Policy)です。

同一生成元ポリシーを使うことで、異なるオリジンからのリソースへのアクセスに制限をかけることができます。 結果的に、クロスサイトリクエストフォージェリ(CSRF)などのセキュリティリスクを防ぐことができます。
CORSはこの制限を一部解除し、異なるオリジン間でリソースを共有できるようにするための仕組みです。

今回はフロントエンドとバックエンドという異なるオリジンでデータをやりとりするので、CORSの設定が必要になります。(ポート番号が異なるため同じオリジンではない扱いになる)

Rails APIは、rack-corsというGemを使用することでCORSの設定ができます。

github.com

今回は、Gemを導入した状態から進めます。

config/application.rbで以下の設定を追加します。

config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:3000'
    resource '*', 
        headers: :any, 
        methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end

originsは、許可するオリジンを指定します。
resourceは許可するリソースファイルを指定します。*とすることで、すべてのリソースファイルを許可します。
headersは許可するHTTPヘッダーのこと。anyとすることですべて許可しています。
methodsは許可するHTTPメソッドを指定します。

CORSを用いると、異なるオリジンにCRUDリクエストを送る際、そのリクエストが許可されていることをサーバに一度確認してからでないと、リクエストを送ることができなくなります。 このリクエストのメソッドにoptionsが使用されます。 CORSを有効にするとこのプリフライトリクエストのために、optionsメソッドが必要なので、methods内の:optionsを削除すると動かなくなるので削除しないようにしてください。

注意点

ここでRailsAPIモードにしてフロントエンドとバックエンドを分ける場合の注意点について少し触れておこうと思います。

APIを実行するとActionController::InvalidAuthenticityTokenCan't verify CSRF token authenticityというエラーが発生することがあります。 これはRailsCSRF対策によるものです。

CSRFとは自分がログインしている(常時ログインなどで)サービスへ、知らぬ間にリクエストを送らされる攻撃です。

通常、常時ログインなどでは、cookieにセッション情報が格納されますが、Railsではリクエストのたびにcookieを自動的に取得します。 cookieはブラウザもしくはサーバに保存されているので、別サイトからリクエストが送られたときにも正しいcookieが使用されてしまうのです。

RailsではCSRF対策を自動生成してくれています。 それがapplication_controllerが作られるときに自動で書き込まれるprotect_from_forgeryメソッドです。

RailsではViewでform_forやform_tagを使うと、自動でCSRF対策用のトークンを埋め込んでくれています。 そして、protect_from_forgeryによって、アクション実行前に正しいトークンが付随されているかをチェックしています。
しかし、今回のようにフロントエンドとのやりとりの場合には自分で対策をしなければいけません。

とりあえずエラーが出ないようにするためであれば、protect_from_forgeryをControllerに設定すれば、チェックを行わないのでエラーが発生しないようにはなります。
ただし、根本的な解決ではないです。 リクエスト時にヘッダにX-CSRF-Tokenを追加し、そこにトークンの値を埋めて送るとRailsサーバーはその値を使ってprotect_from_forgeryを実行してくれるので、この方法が望ましいです。

class UsersController < ApplicationController
      // これは根本的な解決ではない
      protect_from_forgery
      def index
        users = Post.order(created_at: :desc)
        render json: { status: 200, message: "API Success", data: users }
      end
end

参照

prograshi.com

learning-mind.hatenablog.com

qiita.com

qiita.com

web-bonsai.hatenablog.com

zenn.dev