川獺の外部記憶

なんでも残しておく闇鍋みたいな備忘録

axiosの使い方 - headerの付与とか色々

以下のページからREST APIを叩く上でのベストプラクティス(と勝手に思っているもの)を抽出します。

github.com

リクエストを投げるたびにheaderなどを設定する例

GET

kawauso-lab.hatenablog.jp

先日のエントリ(↑)で使用したコードのaxios部を取り出すと下記な感じです。

import axios from 'axios';

const API_BASEURL = "http://127.0.0.1:8000/api/"

axios.get(API_BASEURL + "books/")
  .then((response) => { console.log(response); })
  .catch((error) => { console.log(error); })
  .finally(() => { })

このコードを実行したときのGETパケットは以下のようになります。(画像はクリックで拡大します。見比べる場合には別のウィンドウで開くと見やすいです)

特に設定していないため、もちろん「Authorization」ヘッダが付与されていないのがわかります。(わからなければ次のコード例の実行時スクショと比べてください。)

f:id:marineotter:20200118220448p:plain:w500

今回はここにJWTトークンを使った認証(Django REST framework JWT)を組み込みます。
まずはaxios.get()の引数に毎回 {headers: ...} を入れる方針。

import axios from 'axios';

const API_BASEURL = "http://127.0.0.1:8000/api/"
const API_TOKEN = "TOKENVALUEEXAMPLE"

axios.get(API_BASEURL + "books/", { headers: { Authorization: "JWT " + API_TOKEN } })
  .then((response) => { console.log(response); })
  .catch((error) => { console.log(error); })
  .finally(() => { })

このコードを実行したときのGETパケットは以下のようになります。 ちゃんとAuthorizationヘッダが付与されているのがわかります。

f:id:marineotter:20200118220558p:plain:w500

POST

まず最初に、tokenを付与しない例。

import axios from 'axios';

const API_BASEURL = "http://127.0.0.1:8000/api/"
const API_TOKEN = "TOKENVALUEEXAMPLE"

axios.post(API_BASEURL + "users/", {employee_id: "123456"})
  .then((response) => { console.log(response); })
  .catch((error) => { console.log(error); })
  .finally(() => { })

生パケットは以下。データがちゃんと入っているが、もちろん「Authorization」ヘッダは入っていない。

f:id:marineotter:20200118221213p:plain:w500

次に、GETのときと同じようにtokenを付与する例。

import axios from 'axios';

const API_BASEURL = "http://127.0.0.1:8000/api/"
const API_TOKEN = "TOKENVALUEEXAMPLE"

axios.post(API_BASEURL + "users/", { employee_id: "123456" }, { headers: { Authorization: "JWT " + API_TOKEN } })
  .then((response) => { console.log(response); })
  .catch((error) => { console.log(error); })
  .finally(() => { })

付与した結果は以下のようになります。

f:id:marineotter:20200118221751p:plain:w500

ちなみに
間違えて以下のように書くと、

axios.post(API_BASEURL + "users/", { employee_id: "123456", Authorization: "JWT " + API_TOKEN })
  .then((response) => { console.log(response); })
  .catch((error) => { console.log(error); })
  .finally(() => { })

パケットは次のようになります。

f:id:marineotter:20200119003113p:plain:w500

HTTP Request Headerではなく、Request Bodyのjsonに「Authorization」要素が入ってしまっています。理解できるとそりゃそうやなって感じだと思います。

先に付与する情報を設定しておく例(default config上書き)

GET

import axios from 'axios';

const API_BASEURL = "http://127.0.0.1:8000/api/"
const API_TOKEN = "TOKENVALUEEXAMPLE"

axios.defaults.baseURL = API_BASEURL;
axios.defaults.headers.common["Authorization"] = `JWT ${API_TOKEN}`;

axios.post(API_BASEURL + "users/", { employee_id: "123456" })
  .then((response) => { console.log(response); })
  .catch((error) => { console.log(error); })
  .finally(() => { })

f:id:marineotter:20200118222824p:plain:w500

POST

import axios from 'axios';

const API_BASEURL = "http://127.0.0.1:8000/api/"
const API_TOKEN = "TOKENVALUEEXAMPLE"

axios.defaults.baseURL = API_BASEURL;
axios.defaults.headers.common["Authorization"] = `JWT ${API_TOKEN}`;

axios.post(API_BASEURL + "users/", { employee_id: "123456" })
  .then((response) => { console.log(response); })
  .catch((error) => { console.log(error); })
  .finally(() => { })

f:id:marineotter:20200118222837p:plain:w500

先に付与する情報を設定しておく例(axiosのインスタンスを分ける)

この方法はAPIの種類ごとにヘッダを付与するかどうか分ける必要があるときに便利そうです。(ex: トークン発行関連のAPIを呼ぶときだけはAuthorizationヘッダを付けないようにしたい、など)

同じなのでPOSTのコード例だけ下記に示します。

import axios from 'axios';

const API_BASEURL = "http://127.0.0.1:8000/api/"
const API_TOKEN = "TOKENVALUEEXAMPLE"

const generalApiInterface = axios.create({
  baseURL: API_BASEURL,
  headers: {
    "Authorization": `JWT ${API_TOKEN}`
  }
})

generalApiInterface.post(API_BASEURL + "users/", { employee_id: "123456" })
  .then((response) => { console.log(response); })
  .catch((error) => { console.log(error); })
  .finally(() => { })

Reactで実装する際には実際にはこの方法を使用し、generalApiInterfaceをReduxのStoreに突っ込んでおくのが良いと思います。 (トークンの有効期限が切れたときにこのgeneralApiInterfaceを書き換えたいため)

(ソフトウェア開発はこの「この方法を使用したらいい」の判断が難しいです。この判断ができれば実装できたも同然です。)

おまけ

Web系システムのデバッグにはWiresharkがおすすめです。

Wireshark · Download

使いこなすのは難しいかもしれませんが、低レイヤのパケットを生で見られるようになっておくとハマった時に助かります。知識の補強にもなりますので是非。

ブラウザとサーバさん、最近のWeb系技術だと色々おしゃべりしてて見てて楽しいです。