はじめに
vue.js で初めてシングル・ページ・アプリケーションを作成しました。その際に vue router の history mode でつまづいたので共有とともに、適切な方法があればご教示いただければと思います。
問題点
vue router では HTML5 History Mode を用いた history mode というのを設定することができます。
history mode の詳しい情報は公式をご確認いただきたいのですが、vue-router には 2 つのモードが存在します。
1 つが hash モードで下記のように URL に#がつきます。
https://xxx.com/#/tools
もうひとつが hitsory モードで#を取り除いた綺麗な URL にすることができます。しかし、公式にもある通り、history mode では適切な処理をしないと、ユーザが直接 URL で検索した時や、web アプリケーション閲覧中にリロードをした際に、サーバーからは 404 が返ってきてしまいます。
こちらの 404 対策ですが、公式のやり方では 404 にはならず index.html には遷移することができますが、意図したページに遷移することができませんでした。
例えば、
ホーム画面: https://domain.com/
ニュース画面: https://domain.com/news
ツール画面: https://domain.com/tools
と 3 ページ用意したとします。ユーザが直接https://domain.com/tools
と URL を叩いた場合も、index.html に遷移しホーム画面にリダイレクトされてしまいます。本来やりたいことはhttps://domain.com/tools
を入力した場合やツール画面でリロードした際はホーム画面ではなくツール画面に遷移することです。
解決方法
環境
Frontend : Vue.js
Backend : Spring boot(Restful API を作成)
アプリケーション・サーバー : HTTP Server
アプリケーション・サーバーの設定
アプリケーション・サーバーの httpd.conf ファイルに下記を入力します。
RewriteEngine On
RewriteRule ^/"任意のコンテキストパス"/$ - [L]
RewriteRule ^/"任意のコンテキストパス"/((?!.*(\..+)$).*)$ https://domain.com/"任意のコンテキストパス"/?redirect_path=$1
こちらのリダイレクト設定が意味するのは
https://domain.com/"任意のコンテキストパス/"
に一致する URL がリクエストされた場合はそのままhttps://domain.com/"任意のコンテキストパス/
をレスポンスし、 https://domain.com/"任意のコンテキストパス/hoge
がリクエストされた場合はhttps://domain.com/"任意のコンテキストパス/?redirect_path=hoge
がレスポンスされるようにします。
3 行目の((?!.*(\..+)$).*)
は開発者が用意した css や js ファイルまでリダイレクトされるのを防いでいます。
Frontend の設定
router.ts に下記のように設定します。
import Vue from 'vue';
import Router from 'vue-router';
import { authorize } from '@/router/guard';
Vue.use(Router);
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: () =>
import('@/components/templates/Home.vue'),
beforeEnter: authorize,
},
{
path: '/hoge',
name: 'hoge',
component: () =>
import('@/components/templates/Hoge.vue'),
beforeEnter: authorize,
},
{
path: '/fuga',
name: 'fuga',
component: () =>
import('@/components/templates/Fuga.vue'),
beforeEnter: authorize,
},
{
path: '/*', // 該当するページが存在しない場合は404ページに遷移する
name: 'notfound',
component: () =>
import('@/components/templates/NotFound.vue'),
beforeEnter: authorize,
},
],
});
そして guard.ts を作成し、下記のように記述します。
import store from '@/store';
export const authorize = (to: any, from: any, next: any) => {
const queryKey = 'redirect_path';
if (to.query[queryKey]) { // パスが存在している場合(RewriteRule ^/"任意のコンテキストパス"/((?!.*(\..+)$).*)$ https://domain.com/"任意のコンテキストパス"/?redirect_path=$1 の条件に該当する時)
next({
path: `/${to.query[queryKey]}`, // 取得したパスにvue-routerで遷移する
});
} else { // パスが存在しない場合(RewriteRule ^/"任意のコンテキストパス"/$ - [L] の条件に該当する時)
// 任意の処理を記述
}
};
結果
これらの設定を行うことで、例えばhttps://domain.com/contextpath/hoge
とリクエストを送ると、サーバーからはhttps://domain.com/contextpath/?redirect_path=hoge
とレスポンスされ、フロントエンド側ではredirect_path=hoge
の hoge を取得し、hoge のページにルーティングします。仮にhttps://domain.com/contextpath/hogehoge
のように用意されていないページがリクエストされたときは、フロント側で 404 のページに遷移するように今回の例ではFrontend側で設定しています。
まとめ
ほかの vue で作成されたページを観察すると、このようなリダイレクトをしているように見えないのですが、他の方はどのように設定しているのでしょうか??