Blazor (とWebAPI) しか書けなくなった C# プログラマが、WebAssembly も SignalR も使えない縛りの動的 Web サイトを構築しなければならなくなったとき

# Razor View 構文も Razor Pages 構文も (っていうか、ASP.NET Core タグヘルパーとか) 忘れてしまった

自分は C# を使って、Windows 用のデスクトップアプリケーションや、ASP.NET (ないしは ASP.NET Core) をサーバー側実装とした Web アプリケーションを、仕事や趣味で開発している日々を送っています。

とりわけ Web アプリケーションについては、5年くらい前から以降、クライアント側に AngularJS やその後継の Angular を使っての SPA としての実装ばかりとなりました。

そんな経緯もあり、サーバー側は C# (ASP.NET Core) を使用しているにもかかわらず、サーバー側ビュー的なものをまったく記述しなくなったため、ASP.NET Core MVC の **Razor View** や、**Razor Pages** の書き方を**すっかり忘れて**しまいました。

いや、"@" マークに続けて C# 構文を書けるなどの、基本的な書き方は忘れていません。
ですが、とりわけ、ASP.NET Core タグヘルパーの類いがまったく操れなくなってしまいました。

そんなこんなで、サーバー側は Web API しか書かない日々が長く続きました。

# Blazor 登場

そうこうしているうちに、2～3年前から、クライアント側も C# で実装する SPA 技術「Blazor」が登場しました (公式リリースは今年 2020 年の 5 月ですけれども)。

その流れで、自分は、クライアント側実装を Angular から Blazor WebAssembly へと徐々に切り替え始めました。

そのような経緯で、Blazor コンポーネントは、まずますはそれなりに記述・実装できるようになりました。

# WebAssembly も SignalR も使えない縛り

さてこのような身の上の自分が、もしも、以下のような要件の Web サイト構築の必要に迫られたらどんな選択肢があるでしょうか?

- IE11 に対応必須で、また、総コンテンツサイズが大きくなること (十数MB)、ひるがえって初期ロード時間がかかることから、**Blazor WebAssembly は採用不可**
- タブブラウザでしばらく裏のタブに回っていて休眠状態になっている状況から、アクティブなタブに切り替わったときなどのシナリオで、SignalR 接続が切れてしまうことがあり、これを許容できないため **Blazor Server は採用不可**
- 構築する Web サイトには、ユーザーとの対話要素や動的な表示切り替え要素はない
- ただし、サーバー側のデータベースの内容を読み取ってページに表として表示する、みたいな、サーバー側での動的レンダリングは必要 (静的サイトではない)
- また、この Web サイトの各ページは、**インターネット検索エンジン**に**クロール**してもらえるようにしたい

こんなシチュエーションだと、自分みたいな属性のプログラマであれば、ASP.NET Core MVC や Razor Pages あたりを使った、クラシカルな動的サーバー側 Web サイトとして構築することが多いかと思います。

ですが前述のとおり、自分は、MVC や Razor Pages で実装するのがずいぶんとダルい体となってしまいました。

また、ここでは詳細割愛しますが、自分のケースでは Angular などの JavaScript ベースの解決策も使えない縛りが発生しました（要するに、自分にはそっち方面の技量と経験がなかったのが主な理由)。

# Blazor Server を基本としたサーバー側レンダリング + SignalR なし

しかし、そんな自分のような、Blazor だけにベッタリで潰しが効かないようなプログラマでも、道はあります!

実装は **Blazor Server として実装**し (※Blazor Server はサーバー側初期レンダリングが既定で付いてきます)、しかし、**SignalR 接続は開始しない** (Blazor Server としての動作を起動しない) ようにするのです。

そうするとあら不思議、ASP.NET Core MVC や Razor Pages のような、サーバー側動的レンダリングされる、ページ内リンクをクリックするごとにブラウザによってリンク先のページに対する HTTP GET 要求が発生してページが取得される、古き良き Web サイトとして動作するのです。

もちろん、検索エンジンにクロールしてもらうことも問題ありません。

具体的に手順を見てみましょう。

## 1. まずはプロジェクトテンプレートから普通に Blazor Server アプリを作る

まずは普通に、Blazor Server アプリを新規作成します。

## 2. Blazor としての動作を開始するクライアント側 JavaScript 読み込みを削除!

そうして作成した Blazor Server アプリのコード中、フォールバックドキュメントとして、`_Host.cshtml` という Razor Pages ファイルがあると思います。

この `_Host.cshtml` の中身を見てみると、末尾近くに、JavaScript ファイル `_framework/blazor.server.js` を読み込んでいる script タグの記述があります。

**この JavaScript `_framework/blazor.server.js`** が、ブラウザに (サーバー側で初期レンダリングされた) HTML コンテンツが到着したあと、SignalR でサーバー側との双方向通信を確立し各種ユーザー操作イベントをフック(横取り)してサーバー側でイベント処理するようにする、**Blazor としての息を吹き込む JavaScript** なのです。

なので、この `_framework/blazor.server.js` の読み込みを削除してしまえば、目的達成です。

```html:_Host.cshtml
  ...
  <!-- 👇 下記 script タグを削除! -->
  <!--<script src="_framework/blazor.server.js"></script>-->
</body>
</html>
```

これでクライアント側での動的な処理はなくなります。

サーバー側との SignalR 接続は発生しませんし、`a` タグのリンクをクリックすれば、単純にブラウザがリンク先のページを取得 (HTTP GET) して表示するのみです。

## 3. サーバー側レンダリングを調整

さて、以上で目的は達成できたのですが、最後にもう一押し、サーバー側でのレンダリング内容を調整します。

上記の手順で実装した Web サイトをブラウザで開き、開いたページのソースを見てみると、以下のようなコメント要素が、`body` 要素の直後に追加されているのが確認できます。

```html
...
<body>
<!--Blazor:{"sequence":0,"type":"server","prerenderId":"9e2449206365412bb4b2cb5ef1909c5d","descriptor":"CfDJ8IXNRDMKU0ZOqr94OqxcpcHqWLZQuvRpWrY1/Vl31PYq04tgjlYdz8zNERjiCrAJnUU\u002Br\u002Be6LGY4w8e9iPJULLGiB8wUEbnpGNG/LcbrS9g28kDXi7KqOf4WAgtW611kHcRhHjR/RhS8\u002BBD/hqRjoObtnVcdiWkih1mrg9dDCqwrBeSXAkkAf7/9CV7zvo39BUEi00ggYQeATZUIH51eyPrVZ1rJEnF1caf1tpEBwlwxe/xNuqO5MQEtWsjKB\u002Bkjkwv4n4Z6vNwqkKDzwluBpuzQuakh0m0fPGeNCtZarqAmhH5nlzFXZCa7PmWE3fTGYlcxsbqaz\u002BynqsuVrZh6x6GbPU7jf3\u002BKPWFY5jMpJRdz"}-->
...
```

これは、サーバー側で初期レンダリングしたときの、Blazor コンポーネントの "状態" 情報です。

この HTML コメントの体で挿入された状態情報を、先ほど削除しました `_framework/blazor.server.js` スクリプトが読み取り、SignalR 接続を開設して、サーバー側初期レンダリング時の "状態" を復元して動作を引き継ぐ仕掛けとなっています。

ここまでの手順で、今や、この状態情報を使うものはいなくなりました。

ということで、この状態情報の挿入を差し止めることにします。

やり方は簡単です。
フォールバックドキュメントの `_Host.cshtml` 中に記述されている、サーバー側初期レンダリングを行なうタグヘルパー `component` の属性指定において、**`render-mode` 属性**に `ServerPrerendered` が指定されているところを、**`Static` に書き換え**ます。

```html:_Host.cshtml
...
<component type="typeof(App)" 
  render-mode="Static" /> <!-- 👈 "render-mode" を "Static" に! -->
...
```

これで状態情報の HTML コメント要素がページに埋め込まれることがなくなります。

## 4. 余分なサービスやエンドポイントの登録を削除

動作としてはこれで申し分ないですが、仕上げとして、サーバー側 ASP.NET Core 実装の、不要なサービス登録や、SignalR エンドポイントの登録を削除しておきましょう。

サーバー側の `Startup` クラスにて、まずは `ConfigureService()` メソッド内での、Blazor Server 用サービスを登録削除します。

```csharp:Startup.cs
...
class Startup {
  ...
  void ConfigureServices(IServiceCollection services) {
    ...
    // 👇 下記を削除
    // services.AddServerSideBlazor();
    ...
```

最後に、同じく `Startup` クラスの `Configure()` メソッド内での、Blazor Server 用 SignalR Hub のエンドポイントの登録を削除します。

```csharp:Startup.cs
...
class Startup {
  ...
  void Configure(IApplicationBuilder app, ...) {
    ...
    app.UseEndpoints(endpoints => {
      ...
      // 👇 下記を削除
      // endpoints.MapBlazorHub();
    ...
```

これでスッキリですね!

# まとめ

以上のとおり、WebAssemby は使用不可・SignalR 常時接続も不可、という縛りがあっても、Blazor Server アプリを実装てきる技量さえあれば、クラシカルな・サーバー側動的レンダリングな Web サイトを構築できるようになりました! 😊

Blazor プログラマは、ASP.NET Core MVC の Razor View や Razor Pages の書き方をすっかり忘れていても、これで安心して眠れますね!

_Learn, Practice, Share!_