読者です 読者をやめる 読者になる 読者になる

Carpe Diem

備忘録。https://github.com/jun06t

Angularで作ったサイトでリロードすると404エラー

Angular

概要

Angular2で作ったサイトをS3のstatic website hostingで動かし、

https://www.mysite.com/login

というURLにいる状態でリロードしたところ、Not Foundエラーが出てしまったのでその対応方法をまとめます。

環境

  • angular 2.4.7
  • angular-cli 1.0.0-beta.28.3

なぜこの問題が起きるのか

そもそもURLのパスをルーティングするのはweb server側の仕事であり、Angularで作ったサイトのようにルートにしかindex.htmlがない場合は404エラーになってしまうからです。

URLが変わってるのはなぜ?

HTML5 HISTORY APIpushStateという機能を使っているためです。
ダンWebはAjaxを使い画面遷移をしないでコンテンツなどの入れ替えを行います。
しかしそのままだとURLが変わらず、1つ前の状態に戻りたくても戻るボタンはきちんと機能してくれません。
それを解決してくれるのがこのAPIです。Angularではデフォルトでこの機能を使い、戻るを押した時に以前のパスに戻るようにしてくれます。

ng serveだとリロードできる

実はangular-cliが提供しているツールだと、リロードしてもそのページを表示し続けてくれます。
これは後述する対処法の1つで、内部のweb serverが404エラー時にindex.htmlを見るようにしてくれるからです。

対処方法

a. Webサーバ側で404エラー時にindex.htmlへ流す

使用するWebサーバに依りますが、以下のような方法でできます。

Nginxの場合

error_pageで404エラーの時はindex.htmlを200で返すように設定します。

error_page 404 =200 /index.html;

S3 Static Websiteの場合

エラードキュメントのところにindex.htmlを指定します。

f:id:quoll00:20170209232546p:plain


live-serverの場合

--entry-fileオプションにindex.htmlを指定します。

$ live-server ./dist/ --entry-file=index.html

b. HashLocationStrategyを使う

もう1つの対処法です。
間に#を挟むことで、見た目はURLっぽいですが、実際は常にルートを見ることができます。
つまり

https://www.mysite.com/login

https://www.mysite.com/#/login

とするやり方です。

具体的な実装としてはrouting moduleの中で{ useHash: true }を追記するだけでOKです。

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { TopComponent } from './top/top.component';
import { LoginComponent } from './login/login.component';

const routes: Routes = [
  {
    path: '',
    component: TopComponent
  },
  {
    path: 'login',
    component: LoginComponent,
  },
];
@NgModule({
  imports: [ RouterModule.forRoot(routes, { useHash: true }) ],
  exports: [ RouterModule ],
})
export class AppRoutingModule {}

詳細はこちらをご参考ください。

github.com

まとめ

Angularで作ったサイトでリロードしてもそのページが表示される対応方法を2つ紹介しました。
前者はURLはそのままですが、web server側で設定が必要です。 一方後者はweb serverを弄る必要はないですが、URLにハッシュが混ざるのが違和感あるかもしれないです。

どちらもwork aroundとして一般的に知られている手法なので、好きな方で対応すれば良いと思います。

ソース