Nuxt.jsでステータスコードによって独自の処理とエラーページを使い分けたい

Nuxt.jsには、pageコンポーネント中でエラーが発生した時に遷移するための[エラーページ](https://ja.nuxtjs.org/guide/views/#%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%9A%E3%83%BC%E3%82%B8)が用意されています。



404用のページなどを用意する必要がないので非常に便利ですが、

「ログインの失敗時に、401(認証エラー)なら画面上部にメッセージを出す、500(サーバーエラー)ならNuxtのエラー画面に遷移する」

といった処理をしたいときは、全てのエラー時にエラーページに遷移されたら困ります。

このように、エラーのステータスコードによって、独自の処理とNuxtのエラーページを使い分けるための処理を書いてみました。



## 例：　ログインページ
認証エラー(401)→独自の処理
その他エラー(401以外)→エラーページへ遷移する。かつ、ステータスコードをエラーページで表示させる

## 全体のコード(解説は後ほど)

pages/login.vue

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

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

```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にログイン情報が渡されます。

```js

      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に飛びます。



```js
          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に飛びます。

```js

 throw new Error(error.response.status);
```

この時、[エラーオブジェクト](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Error)の第一引数としてステータスコードを渡しておきます。



### 4.pages/login.vue内のcatchへ飛ぶ

```js

      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](https://github.com/nuxt/nuxt.js/blob/dev/packages/vue-app/template/components/nuxt-error.vue)

```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に入っているステータスコードを表示することで、エラーページ中にステータスコードを表示させることができました！