概要
Angular2で作ったサイトをS3のstatic website hostingで動かし、
という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 APIのpushState
という機能を使っているためです。
モダン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を指定します。
※ただしHTTPステータスコードは404をそのまま返すので、OGPはそのパスでは使えません。OGPはステータスコードがエラーコードだと表示しないためです。
live-serverの場合
--entry-file
オプションにindex.htmlを指定します。
$ live-server ./dist/ --entry-file=index.html
b. HashLocationStrategyを使う
もう1つの対処法です。
間に#
を挟むことで、見た目はURLっぽいですが、実際は常にルートを見ることができます。
つまり
を
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 {}
詳細はこちらをご参考ください。
まとめ
Angularで作ったサイトでリロードしてもそのページが表示される対応方法を2つ紹介しました。
前者はURLはそのままですが、web server側で設定が必要です。
一方後者はweb serverを弄る必要はないですが、URLにハッシュが混ざるのが違和感あるかもしれないです。
どちらもwork aroundとして一般的に知られている手法なので、好きな方で対応すれば良いと思います。