Nuxt.jsには、pageコンポーネント中でエラーが発生した時に遷移するためのエラーページが用意されています。
404用のページなどを用意する必要がないので非常に便利ですが、
「ログインの失敗時に、401(認証エラー)なら画面上部にメッセージを出す、500(サーバーエラー)ならNuxtのエラー画面に遷移する」
といった処理をしたいときは、全てのエラー時にエラーページに遷移されたら困ります。
このように、エラーのステータスコードによって、独自の処理とNuxtのエラーページを使い分けるための処理を書いてみました。
例: ログインページ
認証エラー(401)→独自の処理
その他エラー(401以外)→エラーページへ遷移する。かつ、ステータスコードをエラーページで表示させる
全体のコード(解説は後ほど)
pages/login.vue
<template>
<div>
<header>
<div>
<p>ログイン</p>
</div>
</header>
<div>
<p>登録ID</p>
<input v-model="id" type="text" placeholder="ログインID" />
<p>パスワード</p>
<input v-model="pass" type="password" placeholder="8文字以上" />
<button @click="handleClickLogin">
ログイン
</button>
</div>
</div>
</template>
<script>
import { mapGetters, mapActions } from "vuex";
export default {
data() {
return {
id: "",
pass: "",
message: null
};
},
methods: {
async handleClickLogin() {
try {
await this.clientLogin({ id: this.id, pass: this.pass });
} catch (e) {
this.$nuxt.error(e.message);
}
},
...mapActions(["clientLogin"])
}
};
</script>
store/index.js
export const state = () => ({
token: null,
ID: null
});
export const getters = {
token: state => state.token,
ID: state => state.kenpoID
};
export const mutations = {
setClient(state, { ID, token }) {
state.ID = ID;
state.token = token;
},
};
export const actions = {
async clientLogin({ commit, dispatch }, params) {
try {
const response = await this.$axios.post(`/api/client/login`, params);
const { token, ID } = response.data;
commit("setClient", { ID, token });
this.$router.push(`/clients/${ID}`);
} catch (error) {
if (error.response.status == "401") {
//401の時の処理をここに書く
//実際はフラッシュメッセージを呼び出しましたが今回は省略
} else {
throw new Error(error.response.status);
}
}
}
}
pages/error.vue
<template>
<div>
<div>
<div>
<h1 v-if="error && error.statusCode === 404">
{{ error.statusCode }}: ページが見つかりません
</h1>
<h1 v-else-if="error && error.message">
{{ error.message }}: エラーが発生しました
</h1>
</div>
</div>
</div>
</template>
<script>
import { mapActions } from "vuex";
export default {
layout: "defaultForError",
props: {
error: {
type: Object,
default: null
}
}
};
</script>
全体の流れ
1. pageでのログイン処理
pages/login.vue中で「ログイン」をクリックすると、handleClickLoginが呼ばれclientLoginにログイン情報が渡されます。
methods: {
async handleClickLogin() {
try {
await this.clientLogin({ id: this.id, pass: this.pass });
} catch (e) {
this.$nuxt.error(e.message);
}
},
...mapActions(["clientLogin"])
}
2.clientLoginでログイン情報をpost
store中でログイン処理が成功した場合は、帰ってきたIDとトークンをstateに保存して、個々のユーザーページに遷移します。
何らかのエラーが発生した時はcatchに飛びます。
async clientLogin({ commit, dispatch }, params) {
try {
const response = await this.$axios.post(`/api/client/login`, params);
const { token, ID } = response.data;
commit("setClient", { ID, token });
this.$router.push(`/clients/${ID}`);
} catch (error) {
if (error.response.status == "401") {
//401の時の処理をここに書く
} else {
throw new Error(error.response.status);
}
}
}
3.catch内で、error中のステータスコードによって処理を振り分ける
401の場合は、ユーザーに認証エラーである旨を伝えて再度ログインを試みてほしいのでエラーページには飛ばしません。フラッシュメッセージを出すなどの処理をここに書きます(省略)。
その他のエラーの場合はエラーページに遷移させたいので、まずはstore内で例外を発生させて、handleClickLoginのcatchに飛びます。
throw new Error(error.response.status);
この時、エラーオブジェクトの第一引数としてステータスコードを渡しておきます。
4.pages/login.vue内のcatchへ飛ぶ
methods: {
async handleClickLogin() {
try {
await this.clientLogin({ id: this.id, pass: this.pass });
} catch (e) {
this.$nuxt.error(e.message);
}
},
...mapActions(["clientLogin"])
}
このcatch(e)のeには、先ほどthrow new Errorで投げたエラーオブジェクトが入っています。
先ほど第一引数として渡したステータスコードはe.messageで取得できるので、ステータスコードを引数としてthis.$nuxt.errorでNuxtのエラー画面を呼びます。
5.エラー画面での処理
先ほど引数e.messageとして渡したステータスコードは、errorオブジェクトのmessageプロパティに入っているので、error.messageで取得できます。
参考: https://github.com/nuxt/nuxt.js/blob/dev/packages/vue-app/template/components/nuxt-error.vue
<h1 v-if="error && error.statusCode === 404">
{{ error.statusCode }}: ページが見つかりません
</h1>
<h1 v-else-if="error && error.message">
{{ error.message }}: エラーが発生しました
</h1>
上半分のerror.statusCodeは、ルーティングにないパスを指定すると呼ばれる部分です。
Nuxtにあらかじめ入っている機能を使うために残しています。
下半分は、this.$nuxt.errorを経由してmessageプロパティ付きのエラーオブジェクトが呼ばれた時の処理です。error.messageに入っているステータスコードを表示することで、エラーページ中にステータスコードを表示させることができました!