3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

脳死でaxiosを使うのを卒業して、fetch APIを使ってみた

Posted at

Twitterでいつも参考にさせていただいている、よわよわエンジニアさんが「クライアントライブラリを脳死でAxiosを使う駆け出し勢があまりにも多過ぎる」件について発信をしていました。

見事に自分もその中の一人でしたので、これを機にFetchAPIに触れてみました。

参考↓
https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch

https://blog.capilano-fw.com/?p=6646

開発環境

Laravel8(APIモード) × Vue2(SPA)
現在axiosで記述しているコードをfetch APIで書き直す形式で進めていきます。
何はともあれコードを書いていきます。

GETメソッド

Controller(Laravel)

public function index()
    {
        $users = USER::all();
        return response(['users' => $users, 'message' => 'Retrieved successfully'], 200);
    }

axios(元のコード)

axios
	.get("http://localhost:8000/api/user")
	.then((response) => {
	    this.users = response.data.users;
	    console.log(this.users);
	})
	.catch((response) => console.log(response));

fetch APIで書き直し

fetch('http://localhost:8000/api/user')
  .then(response => {
	  console.log(response);
      return response.json();
  })
  .then(data => {
      this.users = data.users;
      console.log(data.users);
  })
  .catch(error => {
      console.log(error);
  });

console.logの結果を確認

スクリーンショット 2023-01-06 12.07.58.png

.thenを2回使用しているのは、1回目の.thenresponse.jsonがPromiseだから。

promiseチェーンの仕組みによって、結果がバンドラのチェーンに沿って渡され、次のPromiseに引き継がれます。Promiseの理解も怪しい…汗

参考;https://ja.javascript.info/promise-chaining

★ここで抑えておきたいポイント

  • fetch()はPromiseのオブジェクトを返す(response.json()の戻り値もPromise)
  • responseはJSONデータが格納されているresponseオブジェクト
  • JSONデータの加工にはjson()メソッドを使う必要がある

POSTメソッド

前提として、私が作業している開発環境では認証が必要です。認証について触れることで勉強になりました。

Controller(Laravel)

public function store(Request $request)
    {
        $data = $request->all();

        $validator = Validator::make($data, [
            'name' => 'required|max:255',
            'year' => 'required|max:255',
            'company_headquarters' => 'required|max:255',
            'what_company_does' => 'required'
        ]);

        if ($validator->fails()) {
            return response(['error' => $validator->errors(), 'Validation Error']);
        }

        $ceo = CEO::create($data);

        return response(['ceo' => new CEOResource($ceo), 'message' => 'Created successfully'], 200);
    }

axios(元のコード)

const data = {
  name: this.name,
  company_name: this.company_name,
  year: this.year,
  company_headquarters: this.company_headquarters,
  what_company_does: this.what_company_does
};

axios.post('api/ceo', data)
  .catch(function (error) {
    console.log(error);
  })

fetch APIで書き直し

const data = {
  name: this.name,
  company_name: this.company_name,
  year: this.year,
  company_headquarters: this.company_headquarters,
  what_company_does: this.what_company_does
};

const token = sessionStorage.getItem('access_token');

fetch('http://localhost:8000/api/ceo', {
    method: 'POST',
    headers: {
			'X-CSRF-TOKEN': '{{ csrf_token() }}',
      "Accept": "application/json",
      'Content-Type': 'application/json',
      Authorization: `Bearer ${token}`
    },
    body: JSON.stringify(data)
  })
  .then(response => response.json())
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.log(error);
  });

console.logの結果を確認
スクリーンショット 2023-01-07 16.01.56.png

重要なのが、headersに記述しているAurthorizationです。

参考:https://www.appsloveworld.com/reactjs/200/428/post-http-localhost3000-api-v1-stories-401-unauthorized

axiosではデフォルトのヘッダー情報をapp.jsに設定をしています。

// sessionStrageに保存しているAPI Tokenを取得
const api_token = window.sessionStorage.getItem('access_token');
// デフォルトでヘッダーに認証情報を付与
axios.defaults.headers.common['Authorization'] = "Bearer ".concat(api_token);
// 非同期でサーバーとやりとりするために使う
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';

なので、axiosでは毎回Authorizationの記述をする必要がありません。

axios同様、fetch APIでも同様の設定をしたいのですが未解決で時間がかかっているため、後日記事を編集する形で投稿したいと思います。

Headerの中身を深掘り

X-CSRF-TOKEN

そもそもCSRFとは?

webアプリの脆弱性を突いて攻撃する方法のこと。攻撃者のメリットは、攻撃者が自ら処理を実行することなく、ユーザーを介して処理を実行させることができる点。

例)攻撃者が攻撃用のWebページを作成、ユーザがアクセス→攻撃用のリクエストがアクセスしたユーザから攻撃対象に送信→攻撃されたwebアプリケーションが不正リクエストを処理、意図しない処理が実行される

参考:https://qiita.com/mpyw/items/0595f07736cfa5b1f50c

CSRFと密接な関係にあるOriginについて触れます。
Originとは

  • スキーム:https
  • ホスト:example.com
  • ポート:443
    これらをまとめてOriginと呼びます。

CSRF対策として重要なポイントはHTTPリクエストを送らせないことです。

リクエストの結果表示はブラウザのデフォルト機能で防御することが可能(同一オリジンポリシーの仕組み)なため、開発者はアプリケーションが悪意のあるユーザからのHTTPリクエスト自体を送信させない対策が必要です。

そこでCSRFトークンが登場します(やっと)。
CSRFトークンとは、正規のページからアクセスが行われていることを証明するための値です。
X-CSRF-TOKENをヘッダー情報に付与することで、悪意のあるユーザと正規のユーザをAPIが判定することができます。

ただ、他にもストレージにトークンを保持させることで判定する方法もあったりするので、そこの違いについても今後理解を深めていきたいです。

Accept

クライアント側がどんなデータを処理できるかを表す。

今回の実装に関しては、Acceptがなくても実行可能なので、省略可能です。

Content-Type

リクエスト時にメディアタイプ(MIMEタイプ)を指定する役割を果たしています。

Content-Typeを指定しない状態でPOSTすると、私の環境ではValidationエラーが返ってきます。APIがJSONのリクエストを期待しているのに、メディアタイプが指定されていないとメディアタイプを判定できないためです。

Content-TypeにもMIMEタイプという種類があって、リクエストの内容によってタイプが変わります(今回はapplication/json)。
基本的にはContent-Typeのヘッダは必須と考えて記述することが望ましいようです。

参考:https://tech.stmn.co.jp/entry/2021/03/15/183722
参考:https://blog.development-network.net/ung/プログラミング/content-type.html

Authorization

認証を受けるための証明書を保持します。
LaravelPassportで認証を強制しているので、今回はヘッダー情報にAuthorizationは必須となります。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?