Nuxtでaxiosの404がdevとbuildで挙動が違う
まえがき
Nuxt で asyncData や fetch 関数で axios
を使って通信するは特に気にすることではないのですが、 SPA
や SSR
などで Nuxt が提供する関数以外の関数で通信を行った場合に、 404 でうまいことエラー画面に遷移してくれないことがあります。
created
や mounted
などのような Vue.js が提供する関数や、 @click="clickHandler"
のようなイベントハンドラー内だと
$ nuxt dev
では axios で 404 をキャッチした場合は layouts/error.vue
に遷移されるが、
$ nuxt build or generate
$ nuxt start
だと、遷移されません。
これをどうやって挙動を同じにするかをメモしておきます。
nuxt.config.js
の設定は以下のような状態になります。
export default {
ssr: false,
target: 'server', // defalut value
}
サンプルページを作る
pages/request-error-sample.vue
script 部分だけを書いています。
created や click イベントハンドラーのところに axios を使った通信処理を書いています。
どちらの場合も nuxt dev
ではエラーページに遷移し、 nuxt build
ではエラーページに遷移してくれません。
import Vue from 'vue';
import { mapActions } from 'vuex';
export type DataType = {
value: string;
};
export default Vue.extend({
data(): DataType {
return {
value: 'initial value',
};
},
mounted(): void {
},
async created() {
// 404 エラーが返ってくる想定
this.value = await this.$axios.$get(`http://localhost:4000/user/hisasann2`);
},
methods: {
...mapActions({}),
async clickHandler() {
// 404 エラーが返ってくる想定
this.value = await this.$axios.$get(
`http://localhost:4000/user/hisasann2`
);
},
},
});
plugins/axios.ts
plugin を書くことで、 axios の 404 でエラーページに遷移するように error
関数を使う。
$axios.onResponseError
にフックすることでレスポンスのエラーを監視してステータスコードがどうだったらみたいな処理は書けます。
ただし、たとえば 404 で監視すると axios の通信で 404 が発生したら無条件にエラーページに行ってはしまうので、そこは気にする必要があるかもしれません。
import { Plugin } from '@nuxt/types';
import { AxiosError } from 'axios';
const axiosPlugin: Plugin = ({ $axios, app, req, error }): void => {
/**
* $axios.onResponseError
*/
$axios.onResponseError((response: AxiosError): void => {
console.log('$axios.onResponseError');
// 通信エラー
if (!response.response) {
return;
}
const { status, statusText, config } = response.response;
// 404
if (status === 404) {
const message = statusText;
// ここで error 関数を呼ぶと axios の 404 が発生したら必ずエラーページに遷移する
// error({ statusCode: status, message })
}
});
};
export default axiosPlugin;
plugins/error-handler.ts
このパターンは、 Vue.config.errorHandler
にフックすることで、 axios の 404 だけにフォーカスせずに Vue のどんなタイミングでキャプチャされたかもコミコミで監視して error 関数を呼び出す方法です。
import axios from 'axios';
import Vue from 'vue';
export default function ({ error }: any) {
// via https://qiita.com/clomie/items/73fa1e9f61e5b88826bc
// Vue.config.errorHandler でフックできないものたちは以下の2パターンで監視するのもありです。
window.addEventListener('error', (event) => {
console.log('Captured in error EventListener', event.error);
});
window.addEventListener('unhandledrejection', (event) => {
console.log('Captured in unhandledrejection EventListener', event.reason);
});
Vue.config.errorHandler = (err, vm, info) => {
console.log(`Captured in Vue.config.errorHandler: ${info}`, err);
if (axios.isAxiosError(err)) {
if (!info.match(/(created|mounted)/)) {
console.log('!created|mounted:', err);
return;
}
console.log('created|mounted:', err);
// axios のエラーで、且つ created や mounted イベントでキャプチャされた場合
error({ statusCode: err.response?.request.status });
} else {
console.log('axiosのエラーではない:', err);
}
};
}
nuxt.config.js
export default {
plugins: ['@/plugins/axios.ts', '@/plugins/error-hander.ts'],
}
axiosが通信するサーバーを作る
server.js
express を使った 404 を発生させるためのサーバーです。
const express = require('express');
const app = express();
const cors = require('cors');
app.use(cors());
const port = 4000;
app.get('/user/:userId', function (req, res, next) {
console.log('get parameter server', req.params);
if (req.params.userId !== 'hisasann') {
res.status(404);
}
res.send('get parameter client: ' + req.params.userId);
});
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`);
});
$ node server.js
Discussion