VueRouter 404エラー対策


VueRouterを使ったデモアプリを作成

フロントはVue.js、バック(RESTAPI)はSpringBootのデモアプリを作りました
アプリケーションはjar実行しています(SpringBoot組み込みTomcatを利用)

トップページのBLOGボタン押下でブログページを表示します

ブログページはSpringBootで作成したRESTAPIのデータを表示しています

ブログページに表示している記事はRESTAPIで取得しています

ページをリロードしようとすると404エラーが発生

ブログページでリロードしようとすると404エラーで白い画面になってしまいます

この事象はVueRouterのリファレンスでも以下のように説明されています
VueRouterガイド-HTML5 History モード

しかしながら一点問題があります。シングルページのクライアントサイドアプリケーションなので、適切なサーバーの設定をしないと、ユーザーがブラウザで直接 http://oursite.com/user/id にアクセスした場合に 404 エラーが発生します。

router.jsでワイルドカードを指定するだけではダメだった

404エラー時に表示したいエラーページを作成して、router.jsにルーティング設定をしただけでは解消されませんでした

ErrorPage.vue
demo\web\src\pages\ErrorPage.vue
<template>
  <v-app>
    <v-app-bar
        absolute
        color="red lighten-1"
        dark
    >
      <v-toolbar-title>エラーが発生しました</v-toolbar-title>
    </v-app-bar>
    <v-container class="ml-0 mt-16">
      <v-row>
        <v-col cols="12">
          トップページに戻ってください
        </v-col>
      </v-row>
      <v-row>
        <v-col cols="4">
          <v-btn
              color="blue-grey lighten-4"
              width="100"
              v-on:click="clickTopPageBtn"
          >
            top page
          </v-btn>
        </v-col>
      </v-row>
    </v-container>
  </v-app>
</template>

<script>
export default {
  name: "ErrorPage",
  methods: {
    clickTopPageBtn: function () {
      this.$router.push("/")
    }
  }
}
</script>

<style scoped>

</style>


demo\web\src\router.js
import Vue from "vue"
import Router from "vue-router"

import TopPage from "@/pages/TopPage";
import BlogPage from "@/pages/BlogPage";
import ErrorPage from "@/pages/ErrorPage"

Vue.use(Router)

export default new Router({
    mode: "history",
    routes: [
        {
            path: "/",
            name: "トップページ",
            component: TopPage
        },
        {
            path: "/blogPage",
            name: "ブログ",
            component: BlogPage
        },
        {
            path: "*",
            name: "エラーページ",
            component: ErrorPage
        }
    ]
})

Apache .htaccessで解決

Windows用のApacheを導入しました(VueRouter公式ガイドにもWebServerを使えと書いてありましたね…)
Apache+SpringBootJar構成でアプリが動くように設定していきます

AJP通信の設定

今回のアプリはブログページでVue.jsの画面からSpringBootのAPIを呼び出すので
AJP通信を利用してフロントとバックを繋ぎます

ApacheのAJP通信設定

Apacheにはビルド済みVue.jsを配置します
8009ポートでAJP通信できるようにhttpd-proxy-ajp.confを作成します

\httpd-2.4.46-o111h-x86-vc15\Apache24\conf\extra\httpd-proxy-ajp.conf
<Location /blog>
  ProxyPass ajp://127.0.0.1:8009/blog
</Location>

httpd.confでもAJP通信ができるよう修正します

\httpd-2.4.46-o111h-x86-vc15\Apache24\conf\httpd.conf
# 以下のモジュール読み込み箇所のコメントを解除
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
# 末尾に先ほど作成したhttpd-proxy-ajp.confを読み込む設定
Include conf/extra/httpd-proxy-ajp.conf

SpringBootのAJP通信設定

SpringBootの組み込みTomcatでApacheとAJP通信する設定クラスを作成します

demo\src\main\java\com\example\demo\config\AppConfig.java
package com.example.demo.config;

import org.apache.catalina.connector.Connector;
import org.apache.coyote.ajp.AbstractAjpProtocol;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Bean
    public TomcatServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
        Connector ajpConnector = new Connector("AJP/1.3");
        ajpConnector.setPort(8009);
        ajpConnector.setSecure(false);
        ajpConnector.setAllowTrace(false);
        ajpConnector.setScheme("http");
        ((AbstractAjpProtocol) ajpConnector.getProtocolHandler()).setSecretRequired(false);
        tomcat.addAdditionalTomcatConnectors(ajpConnector);

        return tomcat;
    }
}

.htaccess対応

.htaccessにはリダイレクトの設定を記述します
Apacheのドキュメントルートに配置しただけだと.htaccessが適用されません

httpd.confの.htaccess適用設定

httpd.confを以下のように変更します

\httpd-2.4.46-o111h-x86-vc15\Apache24\conf\httpd.conf
# 以下のモジュール読み込み箇所のコメントを解除
LoadModule rewrite_module modules/mod_rewrite.so
# ドキュメントルートのAllowOverride NoneをAllに変更
DocumentRoot "${SRVROOT}/htdocs"
<Directory "${SRVROOT}/htdocs">
    Options Indexes FollowSymLinks
    AllowOverride All
    Require all granted
</Directory>

また、Apacheを配置しているディレクトリにmod_rewrite.soファイルが存在することを確認します
私の環境ではC:\httpd-2.4.46-o111h-x86-vc15\Apache24\modules\mod_rewrite.soでした

.htaccessを作成

VueRouter公式ガイドに記載されている設定内容をそのまま使っています

\httpd-2.4.46-o111h-x86-vc15\Apache24\htdocs\.htaccess
<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

このファイルをApacheのドキュメントルートに配置します
私はC:\httpd-2.4.46-o111h-x86-vc15\Apache24\htdocs\に配置しました

これでApacheとアプリケーションの設定は完了です

ApacheにVue.jsリソースを配置

いつも通りnpm run buildを実行すればOKです

cd C:\Users\risab\git\demo\web
npm run build

package.jsonで特に指定していなければビルド済みリソースは\dist配下に出力されます

出力されたビルド済みリソースをApacheドキュメントルートに配置します

リソースを配置したら、WindowsのサービスからApacheサービスを起動しておきます
Apacheサービス登録は以下のコマンドでできます

cd C:\httpd-2.4.46-o111h-x86-vc15\Apache24\bin
httpd -k install

SpringBootJarを生成

詳細な手順はこちら gradleでjarを生成します

cd C:\Users\risab\git\demo
gradlew bootJar

jarが生成されたら以下のコマンドでアプリケーションを起動します

cd C:\Users\risab\git\demo\build\libs
java -jar demo-0.0.1-SNAPSHOT

これで対策完了!

Apacheにフロントリソースを配置しているのでhttp://localhost/にアクセスします

VueRouterでページ遷移、AJP通信で画面からバックのAPIも呼び出せています
画面のリロード時も404エラーが発生せず、リロードできるようになっています

また、存在しないURLにアクセスした場合は.htaccessとVueRouterのワイルドカードが適用されて
エラーページが表示されるようになりました