【JS】Fetch API のResponse には気をつけて。axios とは違うぞ。。。
昨日、Fetch API を使うにあたり、大きなミスを犯して、無駄な時間を潰してしまったので、反省を込めて、この備忘録を残しておく。
Fetch APIとは
Fetch APIとは、XMLHttpRequestと同じく、HTTPリクエストを送信することができる API である。
ハマった罠
つい axios と同じようなノリで以下のように書いてしまうと、同じ罠にハマることになるので、ご注意を。
await fetch(process.env.API_URL + '/oauth2/token/', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: formDataString }).then((responce) => { console.log(responce) })
これを実行すると、Response には、API の 返却値の中身が入っておらず、Google Chrome の Developer Tools の Network タブで確認しても、Response が空の状態で焦りました。。。Status Code は 200 OK
なのに、どういうことだ???と。
fetch API の定義を TypeScript で確認してみる。
declare function fetch(input: RequestInfo, init?: RequestInit): Promise<Response> // This Fetch API interface represents the response to a request. interface Response extends Body { readonly headers: Headers; readonly ok: boolean; readonly redirected: boolean; readonly status: number; readonly statusText: string; readonly trailer: Promise<Headers>; readonly type: ResponseType; readonly url: string; clone(): Response; } interface Body { readonly body: ReadableStream<Uint8Array> | null; readonly bodyUsed: boolean; arrayBuffer(): Promise<ArrayBuffer>; blob(): Promise<Blob>; formData(): Promise<FormData>; json(): Promise<any>; text(): Promise<string>; }
Response には、body: ReadableStream
でAPIの返却値が入っていて、直接データを参照できないようになっている。。。
response.json().then(data => { // data を使用した処理を実行する })
json()
メソッドを使うことで、Responseストリームを取得して、APIの返却値を取得するようになっていたのである。json()
メソッドを使うことで、ようやくデータを取得することができた。
Fetch API のラッパー関数を作った
axios と同じように使えるように、以下のようなラッパー関数を作ったので参考で紹介しておく。
import cookie from './cookie-storage' const api = { get: async (url: string) => { const response = await fetch(process.env.API_URL + url, { method: 'GET', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + cookie.get('accessToken'), }, }) if (!response.ok) { const err = await response.json() throw new Error(err) } return await response.json() }, post: async (url: string, data: { [key: string]: string }) => { const response = await fetch(process.env.API_URL + url, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + cookie.get('accessToken'), }, body: JSON.stringify(data), }) if (!response.ok) { const err = await response.json() throw new Error(err) } return await response.json() }, } export default api
呼び出して使う場合は、以下のようにする。
import api from 'utils/api' await api .get('/companies/') .then((data) => { setCompanyInfo(data) }) .catch((error) => { console.log(error) })
axios と同じように使えて、これで安心して HTTP リクエストを送ることができる。
まとめ
今回は、Fetch API の使い方に関して、整理を行った。
本来は、axios を使って、Django REST framework のAPIを叩きたかったのであるが、どうしても以下の CORS 問題が解決できなかった為、苦肉の策での Fetch API 利用であった。
Access to XMLHttpRequest at 'http://localhost:8000/api/test/' from origin 'http://localhost:3000' has been blocked by CORS policy: Request header field access-control-allow-origin is not allowed by Access-Control-Allow-Headers in preflight response.
Django側では、django-cors-headers を使っており、Fetch APIでは、CORSのエラーは出ておらず、Django側の設定は問題ないようである。Fetch API も、axios と同じように、異なるオリジンに対してリクエストする時に、OPTIONSメソッドで pre-flight のリクエストを送信する。エラー文にも書いてあるように、ここでエラーが発生している。どうしても解決策が見出せなかった。もし axios、Django に詳しくて、わかる方がいたら、是非教えて欲しい。切に願っている。。。
またこれについても、解決策がわかったら、備忘録としてブログに書いておきたいと思う。
それでは、ステキな開発ライフを。