SPA を IIS から流す際の ASP 側のルーティング
Single Page Application (SPA) にて、トップ以外のページで F5 (page reload/reflesh) を押すと、サーバーから Page Not Found が返ってくる。通常、SPA をレンダーする Web server は、基本的に、GETリクエストに対して、ごく短い 唯一のページ返すだけがお役目。\cart とか \list 等が GET で飛んできても、左様なページ(MVCだとcontroller) は存在しないので、404 エラーとなる。
そこでさっそく調べると、以下を web.config に追加せよ、とある(Web serverは IIS を想定)。
<rewrite>
<rules>
<rule name="Rewrite Text Requests" stopProcessing="true">
<match url=".*"/>
<conditions>
<add input="{HTTP_METHOD}" pattern="^GET$"/>
<add input="{HTTP_ACCEPT}" pattern="^text/html"/>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true"/>
</conditions>
<action type="Rewrite" url="/" appendQueryString="true" />
</rule>
</rules>
</rewrite>
これにて、IIS で走っている Server App には、/ が渡され、デフォルトの Controller がトップページをレンダーする。404 は解決し、唯一のページがレンダーされる。しかし、何も表示しない。Fiddlerすると、/list にて返ってくる html の js や css のパスが、全て /list/dist/bundle.js のように、/list が頭に付与されてしまう。これらは全部 404 となるので、ページは真っ白。
唯一のページの一部:
<link rel="stylesheet" href="~/Content/site.css" />
<script type="text/javascript" src="~/dist/bundle.js"></script>
ようは "~/" が、"/" となるべきところ、"/list" に置き換わり、レンダーされてしまう。私の目が悪いのか、このあたり、ピシッと解説しているのが見つからない。皆さんどうやって解決しているのだろう。/ で決め打ちだったら動作するだろうなという解説は多いが、~/ はこの場合譲れない。virtual site に置かれる可能性がかなり高いから。
あれこれ試したところ、IISのRewriteはあきらめ、ASP アプリのルーティングでとりあえず解決した。こんな感じ。
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
var home = new
{
controller = "Home",
action = "Index",
id = UrlParameter.Optional
};
routes.MapRoute(
name: "list",
url: "list",
defaults: home
);
routes.MapRoute(
name: "doc",
url: "doc/{id}",
defaults: home
);
routes.MapRoute(
name: "signout",
url: "signout",
defaults: home
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: home
);
}
}
IIS での Rewrite をやめたので、ASP の tide の挙動は通常ものに戻り、~/ は常に virtual directory を含めて正確にアプリのルートを示してくれる。上の難点は、F5 が押されたり、Bookmark 等から飛んでくるであろう 正常な Url 全てを定義する必要がある事。ページ数が少ないうちは良いが、、、ベターなやり方があったら、教えてください。
---
次に、Virtual Directory 環境できちんと動作するか、テスト。
VS、Project Property, Web にてIIS Express に Virtual directory を以下のとおり作成。
Project Url: https://localhost:44330/spa
Create Virtual Directory をクリック。
VSにてF5すると、 https://localhost:44330/spa (唯一ページ)がレンダーされるが、React側で、左様なページは無いと叱られた。そうだ、Reactにベース・パスを設定しなければ。これは簡単。
<BrowserRouter basename="/spa"}>
これで叱られなくなったが、先ほど "~/" は譲れないと偉そうなことを申した手前、deploy 毎にこれを書き直すのは武士の一分が立たぬ。そこで、MVCプロジェクトで良くやってきた手を使う。サーバー側の唯一ページのViewに以下を組み込む。
<script type="text/javascript">
var virDir = "@Url.Content("~/")";
</script>
<script type="text/javascript" src="~/dist/bundle.js"></script>
タイミングが良く分からぬが、bundle.js の前に置いた方が安全っぽ。これにて確実に virDirに virtual directory の文字列が格納される。先ほどの記述を、
<BrowserRouter basename={virDir || "/"}>
なのだけど、TypeScript で書いているので左様な変数は無いと叱られる。
declare var virDir: string; // web server to render the virtual dir path
を冒頭に置いてOK。
以上で、綺麗に動いている。production で virtual directory に置かれても問題ないだろう(近々テスト予定)。
コメント
コメントを投稿