0
0

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でAPIエラー発生時、ReduxToolkitのextraReducersでHTTP Status Codeが取れなかった件

Posted at

axiosを使ってAPIコールしextraReducersで結果を処理しようとしていて、
もしAPIが失敗した場合、個別にエラーハンドリングを行うのではなく共通で行いたいと思いました。
400だったらアレしたい、401だったらアレしたいというふうに。

下記のコードのような実装です。

export const slice = createSlice({
  ...省略,
  extraReducers: (builder) => {
    builder.addCase(getMe.fulfilled, (state, action) => {
      state.me = action.payload.me
    })
    builder.addMatcher(
      (action) => action.type.endsWith("/rejected"),
      handleApiRejected
    )
  },
})

export const handleApiRejected = (state: any, action: any) => {
  console.log(action)
}

export const getMe = createAsyncThunk<{ me: Me }>("me", async () => {
  const response = await axiosInstance.get("me")
  return response.data
})

createAsyncThunkで例外が発生した場合、 xxx/rejected みたいな /rejected で終わるアクションがディスパッチされます。

それをextraReducerの builder.addMatcher で捕まえることができます。

そこまではいいのですが、actionの内容は以下のようなものでした。

error: {
  name: 'AxiosError',
  message: 'Request failed with status code 401',
  stack: 'AxiosError: Request failed with status code 401\n  …/./node_modules/axios/lib/adapters/xhr.js:121:66)',
  code: 'ERR_BAD_REQUEST'
}
meta: {arg: undefined, requestId: '41f2ZwUal7bRkxX1fQb-_', rejectedWithValue: false, requestStatus: 'rejected', aborted: false, …}
payload: undefined
type: "getProjects/rejected"

一見、十分なエラー内容に見えるがHTTP Status Codeが無い。
私のやりたかった、ステータスコードをもとにアレやコレをしたいということが実現できないではないか。

message の文字列をパースすれば取れるじゃないかって?
はい、頑張ればできますがそんなことしたくないよね。

このエラーメッセージが作られてるのはどこや

https://github.com/axios/axios の lib/core/settle.js です。

export default function settle(resolve, reject, response) {
  const validateStatus = response.config.validateStatus;
  if (!response.status || !validateStatus || validateStatus(response.status)) {
    resolve(response);
  } else {
    reject(new AxiosError(
      'Request failed with status code ' + response.status,
      [AxiosError.ERR_BAD_REQUEST, AxiosError.ERR_BAD_RESPONSE][Math.floor(response.status / 100) - 4],
      response.config,
      response.request,
      response
    ));
  }
}

ふむふむ。

AxiosErrorの定義を見てみよう

lib/core/AxiosError.js

function AxiosError(message, code, config, request, response) {
  Error.call(this);

  if (Error.captureStackTrace) {
    Error.captureStackTrace(this, this.constructor);
  } else {
    this.stack = (new Error()).stack;
  }

  this.message = message;
  this.name = 'AxiosError';
  code && (this.code = code);
  config && (this.config = config);
  request && (this.request = request);
  response && (this.response = response);
}

なるほど。AxiosErrorオブジェクトとしては this.response として、レスポンスの元となる情報は持っているらしい。

じゃあReduxToolkit側で削ぎ落とされているのか

ビンゴです。

docs/api/createAsyncThunk.mdx こちらを読むに

スクリーンショット 2023-05-18 20.22.49.png

な、なんだって〜...

ここまでで察するに、axios側は、このインターフェースを知っていることになります。
たまたま「name, message, stack, code」という4つのプロパティが偶然に完全一致するとは思えません。
つまりaxios側が敢えて?HTTP Status Codeを数値のまま渡していないということになります。
多分そうなるよね。

その辺りの経緯まで調べるのは骨が折れそうなので諦めました。

結論

ということで、HTTP Status Codeを使って何かしらの分岐をしたい場合は他の方法を使いましょうということでした(雑な締め

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?