30
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Vue.jsのhistory mode 404対策について

Last updated at Posted at 2019-06-24

はじめに

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 ファイルに下記を入力します。

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 に下記のように設定します。

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 を作成し、下記のように記述します。

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 で作成されたページを観察すると、このようなリダイレクトをしているように見えないのですが、他の方はどのように設定しているのでしょうか??

30
28
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?