この連載記事では、フロントエンドに Vue.js + Vue Router + Vuex とサーバーサイドに Laravel を使用したシングルページ Web アプリケーションの開発方法を紹介します。実際に写真共有アプリを開発する手順を通して SPA 開発のエッセンスを学ぶことができます。
今回のチュートリアルで扱うツールなどのバージョンは以下の通りです。
Node | npm | Vue.js | Vue Router | Vuex | PHP | Laravel |
---|---|---|---|---|---|---|
10.15 | 6.4 | 2.6 | 3.1 | 3.1 | 7.4 | 6.9 |
この章では最後の仕上げとして、以下2点のエラーハンドリングを追加します。
- 認証エラー
- 404 エラー
認証エラー
認証エラーは、サイトの閲覧中にセッションが切れるなどして認証していない状態で写真投稿やいいねなど認証の必要な API にアクセスしたときに起こり得ます。
エラーハンドリングの仕組みとしては、システムエラーと同じ方法を採ります。つまり、
- API 通信が完了したあとに失敗であればステータスコードを error ストアに格納する。
- ルートコンポーネントが error ストアに格納されるコードを監視し、コード番号によって必要な処理を行う。
1 および 2 の監視処理はシステムエラーで実装済みなので、基本的には 2 の後半で認証エラーの分岐を増やせば OK です。
トークンリフレッシュ API
認証エラーの場合はログインページへ移動して再度ログインをさせようと考えたのですが、それだけでは課題がありました。ログインページへ移動したあと CSRF トークンがうまくリフレッシュされず、再ログインできなかったのです。
そこで、トークンリフレッシュ用の API を作ってしまいます。
// トークンリフレッシュ
Route::get('/reflesh-token', function (Illuminate\Http\Request $request) {
$request->session()->regenerateToken();
return response()->json();
});
レスポンスコード定義
util.js
にレスポンスコードを定義します。
export const UNAUTHORIZED = 419
認証切れの場合のレスポンスコードは 419 でした。
ちなみに 419 は正式には定義されていないコードで、Laravel が独自で使用しています。
ルートコンポーネント
App.vue
を編集します。
まずレスポンスコード定義 UNAUTHORIZED
をインポートします。
import { UNAUTHORIZED, INTERNAL_SERVER_ERROR } from './util'
そして watch
内の errorCode
のハンドラを以下のように編集してください。
errorCode: {
async handler (val) {
if (val === INTERNAL_SERVER_ERROR) {
this.$router.push('/500')
} else if (val === UNAUTHORIZED) {
// トークンをリフレッシュ
await axios.get('/api/refresh-token')
// ストアのuserをクリア
this.$store.commit('auth/setUser', null)
// ログイン画面へ
this.$router.push('/login')
}
},
immediate: true
},
ログインページに移動する前にストアの user
をクリアしておかないとログインページにアクセスできないので注意しましょう。
これで認証エラー処理は完了です。
404 エラー
404 エラーとして対処すべきケースには2種類あります。
- API から 404 NOT FOUND が返却される。
- ブラウザで存在しないパスにアクセスされる。
レスポンスコード定義
まずは util.js
にレスポンスコードを定義します。
export const NOT_FOUND = 404
エラーページ作成
404 エラーページを作成しましょう。
resources/js/pages/errors/NotFound.vue
を追加してください。
<template>
<p>お探しのページは見つかりませんでした。</p>
</template>
続いてルート定義に <NotFound>
を追加しましょう。
import NotFound from './pages/errors/NotFound.vue'
/* 中略 */
const routes = [
/* 中略 */
{
path: '*',
component: NotFound
}
]
path
に「任意」を意味する *
(アスタリスク)を指定しているのがポイントです。
これにより、定義されたルート以外のパスでのアクセスは <NotFound>
が表示されます。
ルートコンポーネント
API から 404 が返された場合に備えてルートコンポーネント <App>
も編集します。
まずレスポンスコード定義 NOT_FOUND
をインポートします。
import { NOT_FOUND, UNAUTHORIZED, INTERNAL_SERVER_ERROR } from './util'
そして watch
内の errorCode
のハンドラを以下のように編集してください。
errorCode: {
async handler (val) {
if (val === INTERNAL_SERVER_ERROR) {
this.$router.push('/500')
} else if (val === UNAUTHORIZED) {
await axios.get('/api/refresh-token')
this.$store.commit('auth/setUser', null)
this.$router.push('/login')
} else if (val === NOT_FOUND) {
this.$router.push('/not-found')
}
},
immediate: true
},
NOT_FOUND
の場合の分岐を追加しました。
この章はこれでおしまいです。
本章までのソースコードはリポジトリの ch-16 ブランチに置いてあります。
そしてこれでこのチュートリアルもおしまいです。
フロントエンドに Vue + Vue Router + Vuex とサーバーサイドに Laravel を組み合わせて小さなアプリケーションを作ることができました
いかがだったでしょうか?
ここで習得したパターンを仕事や趣味でさらに応用して、より大きなアプリケーションを作れるようになる、そしてエンジニアとしてもステップアップするきっかけになれば幸いです。
最後に
アプリをネットに公開する
作ったアプリケーションをネットに公開してみたい場合は、Heroku が手軽でオススメです。
公開方法は以下の記事を参考にしてください。
入門Laravelチュートリアル (11) ToDoアプリをHerokuにデプロイする
また、公開する場合は以下のコマンドでフロントエンドをビルドしましょう。
$ npm run production
watch
と異なりファイルの圧縮が行われるので、公開環境にはこちらのビルドを用います。
追加機能案
チュートリアルでは取り上げられませんでしたが、今回作った写真共有アプリをブラッシュアップするならこんな機能がいいかも、というアイディアを載せておきます。
- 投稿者ごとの写真一覧ページ
- いいねした写真の一覧ページ
- 気に入った投稿者をフォローする
- 写真へのタグ付け
- 写真の削除
- いいねボタンやバリデーションエラー表示など、複数のコンポーネントに重複して存在している UI をそれ自体コンポーネントに切り出す
- 画像のロードが遅いので、投稿されたときにサイズが小さいサムネイル版を自動生成する
- 画像のロードが遅いので、Cloud Front などの CDN を使うといいかも
これらの機能や自分で思いついた機能などを追加してみるのも、良い学習になると思います。
関連記事
連載記事(全16回)
Vue + Vue Router + Vuex + Laravelで写真共有アプリを作ろう
- (1) イントロダクション
- (2) アプリケーションの設計
- (3) SPA開発環境とVue Router
- (4) 認証API
- (5) 認証ページ
- (6) 認証機能とVuex
- (7) 認証機能とVuex Part.2
- (8) エラーハンドリング
- (9) 写真投稿API
- (10) 写真投稿フォーム
- (11) 写真一覧取得API
- (12) 写真一覧ページ
- (13) 写真詳細ページ
- (14) コメント投稿機能
- (15) いいね機能
- (16) エラーハンドリング Part.2