📚
Rails + TypeScriptでAjax通信のためのCSRFトークンを取得するテンプレートコード
課題
- Rails + TypeScriptでajax通信を行うときにcsrfトークンを付与せずに通信しようとすると
InvalidAuthenticityToken
になる。- おもむろに手癖で
axios.post("/api/hoge",params)
とかやると踏みがち
- おもむろに手癖で
- CSRFトークンは、ページのheadタグの
meta[name='csrf-token']"
内にあるため、それを取得して返す関数を作成する
回答
export const getCsrfToken = (): string => {
const metaElem = document.querySelector("meta[name='csrf-token']")
if (!metaElem) {
throw Error("meta[name=csrf-token] is not founded.")
}
const csrfToken = metaElem.getAttribute("content")
if (!csrfToken) {
throw Error("csrf token is not set")
}
return csrfToken
}
- axios側の設定
import axios, {AxiosRequestConfig} from "axios";
import {getCsrfToken} from "./getCsrfToken";
const defaultOptions: AxiosRequestConfig = {
headers: {'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-TOKEN': getCsrfToken()}
};
export const client = axios.create(defaultOptions);
作成後は、 client
を各コードで import すれば、呼び出し側はCSRFトークンの存在を意識せずに .get
, .post
, .patch
, .delete
ができるようになる。
ハマりポイント解説
- document.querySelector は
nullかもしれない
ので後続でnullチェックをしないとコンパイルが通らない。 - 大前提として、この要素が取得できないのはバグのことが多い(呼び出し側ではどうすることもできない)ので、関数内で例外を投げてしまう
- 要素が取得できたとしても、getAttributeもまたnullを返すことがあるのでnullチェックをしないとコンパイルが通らない。
- content属性が正しく取得できないのもまた根本的なバグであるため、関数内で例外を投げる
- このようにすることで、呼び出し側は
string
で csrfTokenを取得できるようになるため、呼び出し側でNULLチェックを書かなくても良くなる。
参考
CSRF対策などの詳しい解説は↓
Railsガイド クロスサイトリクエストフォージェリ (CSRF)
Discussion