今回達成すること
今回は、Nuxt.js側でエラーが発生した場合に表示するエラーページを作成します。
完成イメージ
エラーレイアウトを作成する
まず初めに、「layouts」ディレクトリ内に
root $ touch front/layouts/error.vue
エラーの受け取り
Nuxt.jsは、レイアウトディレクトリに
発生したエラーは、props
で受け取ります。
front/layouts/error.vue
<template>
<div>
errorが発生しました
{{ error }}
</div>
</template>
<script>
export default {
props: {
error: {
type: Object,
default: null
}
}
}
</script>
エラーページのレイアウトを指定する
エラーレイアウトは、「layouts」ディレクトリ内にありますが、ページファイルと同じように扱います。
レイアウトを指定しない場合、
今現在、
そこで、空白のレイアウトである
ログアウトページのレイアウト名を変更する
root $ mv front/layouts/logout.vue front/layouts/none.vue
Vuetifyを使用するので、名前を変更した<v-app>
タグを追加します。
front/layouts/none.vue
<template>
<!-- v-app 追加 -->
<v-app>
<nuxt />
</v-app>
</template>
名前を変えたので、ログアウトページのレイアウト指定を変更します。
front/pages/logout.vue
<template>
<div />
</template>
<script>
export default {
// noneに変更
layout: 'none',
// 削除
// layout: 'logout',
async beforeCreate () {
await this.$auth.logout()
this.$router.replace('/')
}
}
</script>
エラーページのレイアウトを指定する
エラーページ
front/layouts/error.vue
...
<script>
export default {
// 追加
layout: 'none',
props: {
error: {
type: Object,
default: null
}
}
}
</script>
確認しよう
存在しないURLにアクセスし、404エラーを出して確認しましょう。
エラー時のレイアウトが変更できています。
エラーページを構築する
下準備ができたので、エラーページを構築していきます。
エラーページには、
- エラーナンバー
- エラーメッセージ
- トップページへのリダイレクトリンク
を含めます。
エラーナンバーを表示する
エラーナンバーは、error
オブジェクトのstatusCode
に入っています。
算出プロパティを追加し、HTML内を丸っと書き換えます。
front/layouts/error.vue
<template>
<!-- 追加 -->
<v-container fill-height>
<v-row>
<v-col cols="12">
<v-card-title class="justify-center">
{{ status }}
</v-card-title>
</v-col>
</v-row>
</v-container>
<!-- 削除 -->
<!-- <div>
errorが発生しました
{{ error }}
</div> -->
</template>
<script>
export default {
layout: 'none',
props: {
error: {
type: Object,
default: null
}
},
// 追加
computed: {
status () {
return this.error.statusCode
}
}
}
</script>
画面にはエラーコードが出力されました。
エラーメッセージを表示する
エラーメッセージは、受け取ったメッセージを日本語に変換して表示します。
i18n
の
front/locales/ja.json
{
...
"pages": {
...
},
"project": {
...
}
},
// 追加
"error": {
"Unauthorized": "認証に失敗しました",
"This page could not be found": "ページが見つかりません",
"Network Error": "サーバーで問題が発生しました"
}
}
message
の算出プロパティを追加します。
- 翻訳した日本語が存在するエラーメッセージには日本語メッセージを出力し、
- そうでない場合は英語のエラーメッセージを出力します。
front/layouts/error.vue
<template>
...
{{ status }}
</v-card-title>
<!-- 追加 -->
<v-card-text class="text-center">
{{ message }}
</v-card-text>
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
...
computed: {
status () {
return this.error.statusCode
},
// 追加
message () {
const msg = this.error.message
const path = `error.${msg}`
return this.$te(path) ? this.$t(path) : msg
}
}
}
</script>
-
this.$te(<パス>)
... 引数のパスが存在する場合true
を返すi18n
のメソッド。参考: Vue i18n
日本語化されたエラーメッセージが出力されました。
iconを追加する
デコレーションとして、メッセージの下に申し訳なさそうなアイコンを追加します。
front/layouts/error.vue
<template>
...
{{ message }}
</v-card-text>
<!-- 追加 -->
<v-card-actions class="justify-center">
<v-icon>
mdi-emoticon-sick-outline
</v-icon>
</v-card-actions>
</v-col>
</v-row>
</v-container>
</template>
...
リダイレクトリンクを追加する
エラー発生後は、ユーザーをホームに移動させる必要があるため、移動リンクを追加します。
401認証エラーの場合は、エラーページがレンダリングされる前に
- Cookieのトークン
- ローカルストレージの有効期限
- Vuexのユーザーオブジェクト
の全てを綺麗にし、ログイン前の状態に戻します。
front/layouts/error.vue
<template>
...
</v-card-actions>
<!-- 追加 -->
<v-card-actions class="justify-center">
<v-btn
icon
x-large
color="myblue"
@click="redirect"
>
<v-icon>
mdi-home
</v-icon>
</v-btn>
</v-card-actions>
<!-- ここまで -->
</v-col>
</v-row>
</v-container>
</template>
<script>
export default {
...
computed: {
...
},
// 追加
async created () {
// 認証エラーの場合はログアウト処理を行う
if (this.status === 401) {
await this.$auth.logout()
}
},
methods: {
redirect () {
return this.$route.name === 'index'
? this.$router.go() : this.$router.push('/')
}
}
}
</script>
トップページでエラーが発生した場合
トップページでエラーが発生した場合、遷移先と現在のURLが一致しているため$router.push('/')
ではページ遷移できません。
そこで、トップページの場合は$router.go()
を使ってリロード処理を行います。
$router.go()
は、本来引数に数字を渡し、ページを戻したり進めたりするVue.jsのメソッドです。
$router.go(1)
... 一つ先のページに進む。$router.go(-1)
... 一つ前のページに戻る。
引数を省略した場合は、現在のページがリロードされます。
これは、内部的にJavaScriptのwindow.history.go
が呼ばれるため、その挙動と一致するためです。
go()
メソッド は、0
パラメータまたは値を指定せずに呼び出すと、現在のページが再読み込みされます。引用: https://developer.mozilla.org/en-US/docs/Web/API/History
これで、エラーページが完成しました。
意図的に認証エラーを発生させる
認証エラー時の挙動を確認するには、ログインした状態でミドルウェアのnull
にするコードを差し込みます。
front/middleware/authenticator.js
export default async ({ $auth, store, route, redirect }) => {
// トップページかつユーザーが存在しない場合、何もしない(layouts/welcome.vue表示のため)
if (route.name === 'index' && !$auth.isUserPresent()) {
return false
}
// テストコードの追加
store.dispatch('getCurrentUser', null)
...
}
ページ遷移を行うと401エラーが発生します。
この時に、Cookieのトークンとローカルストレージが削除されていれば実装がうまくいっています。
確認が取れたら必ず、テストコードは削除しておいてください。
front/middleware/authenticator.js
export default async ({ $auth, store, route, redirect }) => {
// トップページかつユーザーが存在しない場合、何もしない(layouts/welcome.vue表示のため)
if (route.name === 'index' && !$auth.isUserPresent()) {
return false
}
// 削除
// store.dispatch('getCurrentUser', null)
}
ログインページにエラーハンドリングを追加する
エラーページが完成したので、ログイン失敗時のエラー処理を行います。
エラー処理メソッドは、共通メソッドとして
front/plugins/myInject.js
class MyInject {
constructor (ctx) { // app => ctxに書き換え
// this.app = app // 削除
this.app = ctx.app // 変更
// 追加
this.error = ctx.error
}
...
// プロジェクトリンク
projectLinkTo (id, name = 'project-id-dashboard') {
return { name, params: { id } }
}
// 追加
// エラーハンドリング
errorHandler ({ status, statusText }) {
return this.error({ statusCode: status, message: statusText })
}
}
// コンテキルトにerrorを追加
export default ({ app, error }, inject) => {
// { app, error }に書き換え
inject('my', new MyInject({ app, error }))
}
作成したerrorHandler()
メソッドを、
front/pages/login.vue
...
<script>
export default {
...
methods: {
...
// ログイン失敗
authFailure ({ response }) {
// 書き換え
return (response.status === 404)
? this.$store.dispatch('getToast', { msg: 'ユーザーが見つかりません😷' })
: this.$my.errorHandler(response)
// 削除
// if (response.status === 404) {
// this.$store.dispatch('getToast', { msg: 'ユーザーが見つかりません😷' })
// }
}
}
}
</script>
確認してみよう
Railsだけのサーバーを停止し、ログイン時に404以外のエラーが出るようにします。
root $ docker-compose stop api
この状態でログインを行なってください。
エラー画面に遷移すれば正しくメソッドが動いている証拠です。
確認が取れたら全てのコンテナを削除しておきましょう。
root $ docker-compose down
コミットしとく
今回の実装は以上です。
コミットしておきましょう。
root $ cd front
front $ git add -A && git commit -m "add_layouts_error.vue" && cd ..
root $
まとめ
今回はエラーページの実装を行いました。
Nuxt.jsは「layouts」ディレクトリに
細かい設定などいらず、とても便利ですね。
一点だけ注意点は、
ログイン認証 最終話予告
さて次回は、Railsにデモプロジェクト一覧を取得するコントローラーを作成しNuxt.jsで表示させます。
そして、ここまでの変更をデプロイし、ログイン認証の実装を終わります。
次回でログイン認証完走です。ご期待あれー。