今回はバックエンドをLaravel、フロント部分をVueで作成したSPAで404ページを作成する方法を解説します。
はじめに
今回はバックエンドにLaravelを用いています。フロント部分のルーティングにはvue-routerを使用しました。
Laravel×Vueの構成でSPAを構築する際はルーティングはVue-Routerにまかせて、Laravel側のルーティングでは何が来ても同一のviewファイルを表示するようにするのが一般的です。
今回は以下のような設定にしてあります。
<?php
Route::get('/{any}', function () {
return view('index');
})->where('any', '.*');
正規表現を使ってどんなURLにアクセスが来てもLaravelではindex.blade.phpを表示するので、
index.blade.php上にVueアプリを構築していくことになります。
存在しないページにアクセスが来たときに404を返す
まずは一般的な404ページの作成方法です。router.jsで以下のようなパスが設定されているとします。
const routes = [
{
path: '/',
component: Index
},
{
path: "/login",
name: "login",
component: Login,
},
{
path: '/register',
component: Register
},
]
このようなルーティングが設定されているときに存在しないURLへのアクセスを404ページ飛ばすのは簡単です。
router.jsを以下のように編集します。
const routes = [
{
path: '/',
component: Index
},
{
path: "/login",
name: "login",
component: Login,
},
{
path: '/register',
component: Register
},
//ここから追加
{
path: '*',
name: 'NotFound',
component: NotFound
}
//ここまで
]
ルーティングはマッチするものを上から順番に探すので、router.jsに記述されているものになにもマッチしなかったら、NotFoundコンポーネントを表示するようにしています。
(NotFoundコンポーネントの作成は別途必要です。)
動的ルートマッチングで404ページを表示する
全て静的なパス(パラメーターを何も持たない)場合は上記のやり方でOKです。
では次の場合はどうでしょうか?
const routes = [
{
path: '/',
component: Index
},
{
path: "/login",
name: "login",
component: Login,
},
{
path: '/register',
component: Register
},
//ここから追加
{
path: "/folder/:body/:id/detail",
name: 'folderDetail',
component: Folder,
},
//ここまで
{
path: '*',
name: 'NotFound',
component: NotFound
},
]
先程のルーティングに加えて動的ルートと呼ばれるものを追加しました。
今回はrouterのパスに:bodyと:idというパラメーターを渡して、そのIDを持つフォルダーの中身を表示することを想定しています。
このパスにアクセスしたときに表示するFolder.vueコンポーネントを見てみましょう。
<template>
<div class="container">
<p>{{ $route.params.body }}</p>
<p>{{ $route.params.id }}</p>
</div>
</template>
パスに渡されたパラメーターには$route.params.〇〇の形でアクセスできます。
現時点ではパスに渡された:bodyと:idを表示しているだけですね。
下のようなURLにアクセスするとこんな感じに表示されます。


ここではデータベースには上記のようなbodyが"テストフォルダ"でidが"77"のフォルダしか存在しない状態を考えてください。
この状態で存在しないフォルダのURLにアクセスしたらどうなるか見てましょう。
bodyパラメータに"存在しないフォルダ"、idパラメータに"9999"を指定してみました。


データベースに存在しないbodyとIDの組み合わせでもFolder.vueコンポーネントが表示されてしまっています。
これでは運用上困るので、修正していきます。
この問題を修正するにはvue-routerのナビゲーションガードという仕組みを使います。
これを使うと、アドレスバーに打ち込んだURLにアクセスする直前に任意の処理を自動で行えます。
今回使うのはbeforeRouteEnterとbeforeRouteUpdateです。
名前の通り、指定されたアドレスに入る前とあるアドレスから別のアドレスに移動する前(リンクが押されたときなど)に処理を組み込めます。
動的ルートマッチングの結果表示されるコンポーネントを編集していきます。
今回の場合はFolder.vueですね。
<template>
<div class="container">
<p>{{ $route.params.body }}</p>
<p>{{ $route.params.id }}</p>
</div>
</template>
<script>
export default {
beforeRouteEnter: (to, from, next) => {
axios.post("/api/checkFolderExist", {
folder_id: to.params.id,
folder_body: to.params.body
}).then((response) => {
if (response.data.length === 0) {
next({path: '/404'});
console.log('そんなフォルダ無いよ〜')
} else {
next();
}
})
},
beforeRouteUpdate: (to, from, next) => {
//上記と全く同じ処理
},
}
</script>
to.params.idやto.params.bodyとするとアクセスする先のURLに渡されたパラメータを取得できます。
例えば、以下の例では、to.params.bodyで「存在しないフォルダ」という文字列が、to.params.idで9999という数字がアクセスする直前に取得できます。

これらをどうやって使うのかというと、、、
beforeRouteEnterとbeforeRouteUpdateのタイミングで、to.params.idとto.params.bodyを引数にして、Laravel側で作った自作のapiを叩きます。
Route::post('/checkFolderExist', 'FoldersController@checkExist');
public function checkExist(Request $request)
{
$theFolder = Folder::where('id', $request->folder_id)
->where('body', $request->folder_body)
->get()
->sortByDesc('created_at');
return $theFolder;
}
もしこのAPIを叩いた結果、当てはまるものが無かったら(response.data.length === 0だったら)、"/404"ページにリダイレクトさせます。
今回のrouter.jsでは/404を定義していないので結果としてNotFoundコンポーネントが表示されます。

apiを叩いた結果フォルダが取得できればnext();として正常にFolder.vueコンポーネントを表示させます。
最後に
いかがだったでしょうか。VueRouterを用いたSPAでの404ページの表示は一筋縄では行かないというのが今回の感想です。他のやり方もあると思うので何かあればコメントで教えて下さい。
Vue.js関連の記事はこちらもおすすめ