ASP.NET Core Blazor と Chart.js で入門する Web アプリ作成 － 探究編：Blazorの仕組みを理解する

「ASP.NET Core Blazor と Chart.js で入門するWebアプリ作成」の2回目。各回記事の内容は以下のようになります。

[導入編：サンプルを動かす][link-1]
探究編：Blazorの仕組みを理解する（当記事）
[実践編：Chart.jsでグラフを描く][link-3]
[Tips編：BlazorやChart.jsのあれこれ][link-4]
[番外編：Ubuntuサーバで公開する][link-5]

[link-1]:https://qiita.com/okatako/items/171f05dfc36d6b27769d
[link-2]:https://qiita.com/okatako/items/c0208510961982df4bdc
[link-3]:https://qiita.com/okatako/items/4dbb7eaee95f4c337d57
[link-4]:https://qiita.com/okatako/items/e50b90afecaf5ddb5f01
[link-5]:https://qiita.com/okatako/items/b04ddd9c4f5631d0cc40

今回の内容は、第1回「導入編」の「作業の流れ」で提示した項目のうち「3. Blazor フレームワークの主な構成要素を知る」です。

# Blazor フレームワークの主な構成要素を知る
本稿の中では、おそらく今回記事が一番ハードとなります。読んでいるうちに「あ、挫折しそう」と思ったら、迷わず次回記事に移りましょう。いろいろとプログラムをいじりながら、何か疑問が出たらこの記事に戻ってきてください。

この節の内容をより深く理解したい方は、Razor Pages についても知っておくとよいかと思います。この目的には以下のサイトが役立ちました。というか、泥縄ですが筆者は本節を書くのにこちらのサイトで勉強し直しました。

- [進化した「ASP.NET Core 2.0」新しいWeb開発手法を学ぶ](https://codezine.jp/article/corner/718) (ASP.NET Core 2.0 の時代の記事。はじめの2本を読めば歴史と基本がわかる)
- [Learn Razor Pages](https://www.learnrazorpages.com/) （ASP.NET Core 3.1 に対応。Google翻訳を駆使しよう）

Webアプリ初心者（本稿執筆前の筆者）は、たぶん、いきなり上記のサイトを読んでも興味が続かないかもしれません。とりあえずは本稿を読み進め、何かを作る経験をした上で、疑問点だらけの頭にしてから読むと得るものも多いと思います。

## Blazor プロジェクトの全体構成

前回作成したサンプルプロジェクトで、VS のソリューションエクスプローラは下図のようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/941843a6-5577-b717-b95a-91777877f9ab.png)

VS Code の Explorer の表示と比べると、共通するのは以下のものとなります。

- wwwroot/
- Data/
- Pages/
- Shared/
- _Imports.razor
- App.razor
- Program.cs
- Startup.cs

なので、これらが Blazor の主要構成要素であろうと想像できます。これらの要素の役割を見ていきましょう。

## wwwroot
静的なコンテンツ、つまり、いつ GET されても同じ内容を返すファイルを置きます。実際、上記画像では、css と favicon が置かれています。この後、 Chart.js などの JavaScript もここに配置することになります。

_wwwroot_ がURLのルートに対応するので、たとえばブラウザから /css/site.css を GET すると _wwwroot/css/site.css_ の内容が表示されます。

## Data
これは Blazor にとって必須というわけではありません。慣習として、データ的なものはここに置くことになっているようです。上記画像では、「Fetch data」で表示される気温データを生成するプログラムが格納されています。

## Pages
ブラウザに表示されるページを構成するファイルを置きます。Blazor では _Pages_ というフォルダを特別扱いしていて、ここが動的なページツリーのルート("/")に相当するようになっています[^cshtml_1]。

## Shared
ページの「部品」となるコンポーネントなど、共用されるファイルを置きます。

## .cshtml
.cshtml という拡張子の付いているファイルは、ASP.NET で以前からサポートされているもので、 HTML の中に C# のコードを混ぜることで動的にページを作るのに使われます。表示するページを構築（レンダリング）するときにそのコードが実行されるような仕組みになっています。C# のコードを混ぜるには、"@" をプレフィックスとする「Razor 構文」と呼ばれる記法に従います。詳細は「[Razor ASP.NET Core の構文リファレンス](https://docs.microsoft.com/ja-jp/aspnet/core/mvc/views/razor?view=aspnetcore-3.1)」を参照していただきたいのですが、おおよそ以下のように理解していれば大丈夫かと思います。

- `@変数名` で変数の内容をそこに展開する
- `@メソッド名` でメソッドを実行する
- `@for` や `@if` などの制御構文も使える
- `@{ ... }` で完全なC#コードブロックを記述できる。上記の変数やメソッドをこの中で定義できる

.cshtml ファイルの中でも、先頭に `@page` と書いてあるものは ASP.NET Core 2.0 以降で提供されている Razor Pages というフレームワークによってサポートされるファイルとなり、ページを表現することができます。MVC パターンでいうところの V（ビュー）とC（コントローラ）を一つのファイルにまとめて記述できるようになったもの、ということらしいです[^mvvm]。

[^mvvm]: MVVM (Model-View-ViewModel) パターンとも言うらしいです。

実際 Blazor は、この Razor Pages を拡張したものです。 _Startup.cs_ の中を見ると次のようなコードがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/f876cb6f-3f74-a51f-5431-26b8c3d5dd11.png)
ここの `service.AddRazorPages()` により Razor Pages が有効になり、`services.AddServerSideBlazor()` により Blazor が有効になるようです。

`@page` にはそのページのパスを書くこともできます。たとえば、

```html
@page "/foo"
```
とあれば、これは `/foo` というページを表わしていることになります。

`@page` にパスを書かなければ、そのファイルのパスは「_Pages_[^cshtml_1] をルート(Root)とし、ディレクトリ階層をたどり、ファイル名から拡張子を除いたもの」になります。たとえば _Pages/Foo/Bar.cshtml_ ファイルのパスは `/Foo/Bar`[^cshtml_2] になります。

[^cshtml_1]: _Startup.cs_ の中で _Pages_ 以外のフォルダをルートに変更することもできます。https://www.learnrazorpages.com/razor-pages/routing#changing-the-default-razor-pages-root-folder を参照。

[^cshtml_2]: 大文字・小文字は区別されないので、`/foo/bar` も同じファイルを指します。

この、URLで指定されているパスと実際に表示されるファイルを結びつける役割を果たしているのが「ルーティグ」(routing) と呼ばれる機構[^cshtml_3]です。ルーティングという機能は、MVC パターンだとコントローラが担っているかと思うのですが、Razor Pages の場合はフレームワークのほうでよしなに計らってくれるわけですね。

[^cshtml_3]: ルーティング機能は、 _Startup.cs_ の Configure() の中で `add.UseRouting();` を呼ぶことで有効になるようです。

以下、 _Pages_ に含まれる _.cshtml_ ファイルの説明。

<dl>
<dt><i>_Host.cshtml</i></dt>
<dd>ページの大枠を記述する Razor Pages ファイル。先頭が<code>@page "/"</code>なのでルートページとなる。中を見ると分かるが、&lt;head&gt; タグや &lt;body&gt; タグが使われている。ページ内で必要になる css や JavaScript ファイルはここで読み込んでおく。詳細については後述。</dd>
<dt><i>Error.cshtml</i></dt>
<dd>必須ではないが、何かエラーが発生したときに表示する内容を記述している。このページのパスは <code>/Error</code> になるが、<i>Startup.cs</i> の <code>Configure()</code> の中で例外ハンドラーとしてこのページを指定している。
</dd>
</dl>


## .razor
_.razor_ という拡張子の付いているファイルは、再利用可能なUIコンポーネントを定義するためのファイルです。そしてこの _.razor_ ファイルこそが Blazor フレームワークの中心的役割を果たします。

この部品は「Razor コンポーネント[^razor-component]」と呼ばれます。もちろん、ページ自身もコンポーネントとして定義することができます。ページ自身も含め、いろいろと使い回せるような部品をいい感じに書くことができる仕組みだと考えておけば良さそうです。

[^razor-component]: まったく紛らわしいのですが、_.razor_ という拡張子を持つファイルが Blazor アプリにおけるコンポーネントになります。そして、[正式名称は「Razor コンポーネント」](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/?view=aspnetcore-3.1#components)です。Razor Page と Blazor の勉強を始めた当初は、本当に混乱させられました。

_.razor_ ファイルの記述法は _.cshtml_ ファイルと似ています。ただし以下の違いがあります。

- `@page` 命令を記述する場合はパス指定を省略することができない

`@page` 命令を書く場合は、以下のように必ずパスを指定します。

```html:Counter.razor
@page "/counter"
```
パス指定を省略して（`@page`だけにして）ビルドすると以下のようなエラーになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/2de82971-d6f7-237b-a5c1-b2c269235408.png)
`@page` 命令自体を省略することはできます。`@page` 命令があるとページとして機能し、無ければ部品として機能するようです（後述の _SurveyPrompt.razor_ など）。


- _.razor_ ファイルには `<script>` タグが書けない

無理やり書いてみると VS に叱られます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/2ab7359c-27da-60f2-fd10-553e6c63f045.png)
ちなみに次回の「実践編」では、 Chart.js を呼び出すための `<script>` を _\_Host.cshtml_ に記述しています。

- コードブロックについては `@{ ・・・ }` ではなく、`@code { ・・・ }` と書く

具体例については、下記、_Counter.razor_ を参照。

### サンプルプロジェクトの _.razor_ ファイル
サンプルプロジェクトには以下の _.razor_ ファイルがあります。

<dl>
<dt><i>Pages/Counter.razor</i></dt>
<dd>カウンターページの Razor コンポーネントファイル。</dd><br/>
<dt><i>Pages/FetchData.razor</i></dt>
<dd>左側サイドメニューで「Fetch data」をクリックしたときに表示されるページの Razor コンポーネントファイル。ランダムに生成される気温データを表示する。</dd><br/>
<dt><i>Pages/Index.razor</i></dt>
<dd>ルート("/")ページのコンテンツとなる Razor コンポーネントファイル。</dd><br/>
<dt><i>Shared/MainLayout.razor</i></dt>
<dd>ページのレイアウトを定義する Razor コンポーネントファイル。</dd><br/>
<dt><i>Shared/NavMenu.razor</i></dt>
<dd>左側に表示されるナビゲーションメニューを定義する Razor コンポーネントファイル。</dd><br/>
<dt><i>Shared/SurveyPrompt.razor</i></dt>
<dd>Razor コンポーネントタグとして使う例。</dd><br/>
<dt><i>_Imports.razor</i></dt>
<dd>すべての Razor ファイルでインポートされる。</dd><br/>
<dt><i>App.razor</i></dt>
<dd>アプリケーションの実体となるアセンブリなどの定義や、レイアウトファイルの指定を行う。</dd>
</dl>
以下、一つずつ説明していきます。

### _Counter.razor_
まず _Counter.razor_ を例にして Razor コンポーネントの説明をします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/5b0e076d-3375-3050-5b57-1bf1f6679c9f.png)
先頭に`@page "/counter"` とあるので、このコンポーネントは `/counter` というパスに対するページとして機能することになります。

5行目に `@currentCount` という記述があります。これは典型的な Razor構文の一つで、その下の `@code` 部分で定義されている `currentCount` という変数（フィールド）の値をこの場所に展開します。初期値が `currentCount = 0` になっているので、最初にレンダリングされるHTMLは次のようになります。

```html
<p>Current count: 0</p>
```
最初だけではありません。Blazor では、`currentCount` の値が変化すればそれに応じてブラウザに表示されている `<p>Current count: @currentCount</p>` の部分が動的に上書きされます。

7行目、`button` 要素のところに `@onclick="IncrementCount"` という記述があります。この記述により、ボタンがクリックされた時に `@code` ブロックで定義されている `IncrementCount()` メソッドが呼ばれるのですが、このコード部分はサーバ側で実行されます。そして `currentCount` の値が変更されると、今度はブラウザ側で `@currentCount` の部分が上書きされるのです。

この一連の流れは、SignalR という双方向通信技術を用いて実現しているようです。ページ遷移を起こさず、裏でブラウザとサーバ間で通信を行い、データを送受信してページ内容を書き換えているわけです。

`@code` 部分は、 _.razor_ ファイルと切り離して本来の C# コードファイルに記述することもできます。ちょっとやってみましょう。_Pages_ フォルダ配下に新しいクラスファイルとして _Counter.razor.cs_[^razor_cs] を作成してください。

[^razor_cs]: _.razor_ ファイルと同じ名前で末尾に _.cs_ を付けます。Visual Studio のソリューションエクスプローラでは、本文の図にもあるように _.razor_ ファイルと束ねられて表示されます。

VS Code の場合：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/e2329aae-e585-7cb0-2607-fe5bf8bb532e.png)

VS の場合：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/9b769266-320a-1aa2-f33d-5b2b8f3d02bb.png)

内容を以下のように置き換えます。（違いが分かるようにインクリメント幅を 2 に変更しています）

```csharp:Counter.razor.cs
namespace MyBlazorApp.Pages
{
    public partial class Counter
    {
        private int currentCount = 0;

        private void IncrementCount()
        {
            currentCount += 2;
        }
    }
}
```
名前空間は、{_AppName_}.{_FolderName_} です。上記例では _MyBlazorApp.Pages_ になっています。クラス名をファイル名（コンポーネント名）に一致させます。_.razor_ ファイルのほうでも裏で同名の partial クラスが生成されているので、ここでもクラスに `partial` を付加します。_Counter.razor_ のほうの `@code` の中は削除してください。イメージは次のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/a04dddd0-4947-af3d-56cc-6bb92f2bec4b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/474c115c-f535-e00d-2ee5-a02db6f39631.png)

F5 を押してビルド＆実行します。ブラウザが開いたら Counter 画面に移動します。ここで「Click me」ボタンをクリックすると、"Current count" が 2 になるはずです。上記修正が有効に機能しているのが分かると思います。

### _FetchData.razor_
_FetchData.razor_ についても C# コードの分離をやってみましょう。_Counter.razor_ の場合と同様、 _FetchData.razor.cs_ というファイルを作成します。内容を以下で置き換えてください[^fetchdata_1]。

[^fetchdata_1]: 実際はここで `ComponentBase` からの派生の記述は不要です（_.razor_ ファイルから自動生成される partial クラス定義ですでに派生がなされているため）。 OnInitializedAsync() などがどのクラスで定義されているかを明示するためにあえて記述しました。

```csharp:FetchData.razor.cs
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using MyBlazorApp.Data;

namespace MyBlazorApp.Pages
{
    public partial class FetchData : ComponentBase
    {
        [Inject]
        private WeatherForecastService ForecastService { get; set; }

        private WeatherForecast[] forecasts;

        protected override async Task OnInitializedAsync()
        {
            forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
        }
    }
}
```
_FetchData.razor_ ファイルからは、先頭の `@using MyBlazorApp.Data` および `@inject WeatherForecastService ForecastService` の部分と、``@code`` の中を削除します。イメージは以下のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/cc3690df-68e4-71bf-b213-6727c72f9c66.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/75f26442-0aeb-8caa-65ce-3ad1a251ea35.png)
F5 を押してビルド＆実行します。ここで以下の行にブレークポイントを設定してみてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/a03196f1-9b00-4170-9a10-98e1c95e459d.png)
ブラウザが開いたら Fetch data 画面に移動します。すると、先ほど仕掛けたブレークポイント[^breakpoint]のところで実行が止まっているのが分かると思います。この `OnInitializeAsync()` というメソッドは、ページがレンダリングされる前の初期化の際にフレームワーク側から呼び出されます。何か初期化処理を行いたい場合は、ここでやるようにします[^oninitialize_1]。VS（または VS Code）をアクティブにし、F5 を押して処理を続行させてください。

[^breakpoint]: ここでは分離した _.cs_ ファイルの中でブレークポイントを仕掛けましたが、_.razor_ ファイルのコードブロックでブレークポイントを仕掛けることもできます。

[^oninitialize_1]: アプリによっては、`OnInitializeAsync()` が[2回呼び出される](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/components/lifecycle?view=aspnetcore-3.1#component-initialization-methods)ことがあります。対処方法は次回以降の記事で説明予定。

元の _.razor_ ファイルにあった `@inject` あるいは分離した C# コードの `[Inject]` 属性は、その引数あるいは修飾されている変数に、フレームワークからインスタンスが「注入」されてくることを宣言しています。いわゆる「依存性の注入」(Dependency Injection)と呼ばれる機構です。

注入されてくるインスタンスは、あらかじめフレームワークによってシングルトンとして生成されている必要があります。このことをフレームワーク側に伝えるには _Startup.cs_ の `ConfigureService()` メソッドで `service.AddSingleton<CLASS_NAME>()` を呼び出します。実際のコード例は下記のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/f876cb6f-3f74-a51f-5431-26b8c3d5dd11.png)
上記31行目で `AddSingleton()` が実行されています[^config_svc]。ここの `WeatherForecastService` クラスは _Data/WeatherForecastService.cs_ で定義しています。

[^config_svc]: ここの29行目と30行目は、どんなサーバサイドBlazorアプリでも呼び出されます。31行目の `AddSingleton()` が個々のアプリごとに変わる部分となります。

### _Index.razor_
_Index.razor_ ファイルは、先頭の `@page "/"` で分かるようにルートページのコンテンツを提供しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/45b3a366-e74b-b96e-570a-ff46026cd2db.png)
特徴的なのは最後の行の `<SurveyPrompt ・・・ />` というタグですね。このように Razor コンポーネント名をタグとして使う[^survey_1]と、その場所に指定の Razor コンポーネント（この場合は `SurveyPrompt`）が展開されます。さらには `Title="How is Blazor working for you?"` という形でコンポーネントに値を渡すこともできます。

[^survey_1]: Microsoftのドキュメント「[コンポーネントを使う](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/components/?view=aspnetcore-3.1#use-components)」に説明があります。

### _SurveyPrompt.razor_
では _SurveyPrompt.razor_ を見てみましょう。このファイルは _Shared_ というフォルダに置かれており、まさに「共通部品」という扱いになっています。

まず最初の3行。ここは画面表示されるHTML部となります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/a057b011-2f03-cee6-015a-b127d6d1b348.png)
`@page` 命令が無いのがお分かりでしょうか。前述のように `@page` のない _.razor_ ファイルはコンポーネント（部品）として機能します。また、3行目、`@Title` でプロパティ変数 `Title` を参照しています。
そして末尾の C# コードブロック。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/caffb73a-ef26-8c63-23b9-417c586ac319.png)
ここで `Title` をプロパティとして定義し、かつ `[Parameter]` 属性を付与しています。この `[Parameter]` 属性が付与されたプロパティには、先ほどの `<SurveyPrompt Title="How is Blazor working for you?" />` のような形で値を渡すことができるようになるわけです。そう考えると引数指定の関数呼び出しみたいにも見えますね。

実際にブラウザに表示されている文言を見ると、たしかに `Title` の引数として記述した文字列が渡されていることを確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/7947bc79-9369-884b-1581-24588ed99f3d.png)
Razor コンポーネントがまさに「部品」として使われていることが分かるかと思います。

### _\_Imports.razor_
_\_Imports.razor_ は特殊なファイルです。このファイルが置かれたフォルダおよびそのサブフォルダに配置した _.razor_ ファイルによって暗黙的にインポートされます。たとえば、これまでに説明した _Counter.razor_ や _SurveryPrompt.razor_、また後述する _App.razor_ など、すべての _.razor_ ファイルによってインポートされることになります。サンプルプロジェクトでは、下記のように `@using` 命令を記述しておくことで個々の _.razor_ ファイルでは `@using` を書かかずに済むようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/fb9d0e04-2be1-08ee-078e-9cbd8319c9d0.png)

### _App.razor_, _MainLayout.razor_, _NavMenu.razor_ (and _\_Host.cshtml_)
このあたりから鬼門[^kimon]に突入していきます。

[^kimon]: よく理解できていない要素がいっぱい出てくる、という意味です。

```
_Host -> App -> MainLayout -> NavMenu
                           -> Body
```
というような呼び出し構造になっているので、まずはサイトの入り口となる **_\_Host.cshtml_** を再訪。`<body>`のところだけ掲出します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/b982edac-bde0-1a5d-7ec5-cb4ec2039938.png)
20行目、`<app>`[^apptag] タグに囲まれた部分に `<component type="typeof(App)" render-mode="ServerPrerendered" />` とあります。`component` はフレームワーク側で用意している TagHelper[^taghelper_1] のようです。サーバ側Blazor の `render-mode` には、_ServerPrerendered_ の他に _Server_ と _Static_ があります[^render-mode]。`type`属性の値として`typeof(App)` とあるので、おそらくここに下記 **_App.razor_** の内容が展開されるのでしょう。

[^apptag]: 余談ですが、`<app>` タグは HTML 4.01 の時点ですでに deprecated になってるんですね。そのためか、次の [ASP.NET Core 5.0 では変更されるかもしれません](https://github.com/dotnet/aspnetcore/issues/11013)。

[^taghelper_1]: 正直、筆者は TagHelper の仕組みをよく分かっていないのですが、とりあえずは「何らかのマクロ的なモノ」という理解で進みたいと思います。勉強したい方は、たとえば [Learn Razor Pages](https://www.learnrazorpages.com/razor-pages/tag-helpers/) などを参照ください。

[^render-mode]: `render-mode` の説明は「[ASP.NET Core Blazor ホスティング モデルの構成 - 表示モード](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/fundamentals/additional-scenarios?view=aspnetcore-3.1#render-mode)」にあります。「Tips編」で説明予定。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/84eab7ac-31a1-c182-7ed6-89267cbfccf4.png)
ここで使われている `Router` や `Found` などは _Microsoft.AspNetCore.Components_ 配下で定義されているコンポーネントのようです。

まず `<Router AppAssembly="@typeof(Program).Assembly">` で当アプリの _Program_ アセンブリを指定していると思われます。そして、ルーティングすべきデータがあれば `<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />` により、**_MainLayout.razor_** に記述されているレイアウトに従ってコンテンツを配置する、ということでしょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/6bc8ede0-c123-d2ce-4456-1b56177dcffa.png)
先頭の `@inherits LayoutComponentBase` で、当ファイルから生成されるクラスが `LayoutComponentBase` から派生すべきことを宣言しています。

その後に`<div>`が2つあり、左側に sidebar、右側に main というレイアウトになっています。そして、sidebar の中味が `NavMenu` であること、main の上部に About があって、その下に `@Body` があること、が分かります。

`@Body` で参照されている `Body` は、下図のように派生元である `LayoutComponentBase` クラスに含まれるプロパティのようです。このプロパティを呼び出すと描画するべきコンテンツが得られるということですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/4c4ff4ff-9e44-dfbb-592d-96dff1ba86e2.png)
このレイアウトに従って表示すると下図のようなページが得られるというわけです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/3b541db5-0c4c-e1b7-209d-38079e74571a.png)

終わりは **_NavMenu.razor_** です。3つの部分に分かれています。まずは上部の brand の部分。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/859acd2e-466e-e33c-f381-3c57ea13cc78.png)
アプリ名 MyBlazorApp を表示し（2行目）、それをクリックすると `ToggleNavMenu`メソッドを呼び出すようになっています（3行目）。`ToggleNavMenu`メソッドについては後述するコードブロックの中で定義されています。

次は各ページへのリンクです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/4d855ae4-0965-9d5b-c7c3-31cb089a614a.png)
ここでもクリックすると `ToggleNavMenu` を呼び出すようになっています（8行目）。また class 名として `NavMenuCssClass` プロパティを参照しています。9行目以降、リストアイテムとして Home, Counter, Fetch data が並んでいます。それぞれ `href` として `""`, `counter`, `fetchdata` を指定しているので、ルート(`/`)からの相対パスと考えれば、これは既に見た _Index.razor_, _Counter.razor_, _FetchData.razor_ の `@page` 命令で設定したパスに一致しています。

ここで使われている `NavLink` ですが、ドキュメント「[NavLink コンポーネント](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-3.1#navlink-component)」を見ると次のように説明されています。

>ナビゲーション リンクを作成するときは、HTML ハイパーリンク要素 (&lt;a&gt;) の代わりに NavLink コンポーネントを使用します。 NavLink コンポーネントは &lt;a&gt; 要素のように動作しますが、href が現在の URL と一致するかどうかに基づいて active CSS クラスを切り替える点が異なります。

この文の意味するところをじっくりと考えてみたのですが、おおよそ次のようなことかと思われます。

- `NavLink` は `a` タグのような動作をする（`href` や `target` など `a` タグと同じ属性が使える）
- 表示されているページの URL が `href` で指定したものにマッチしていれば、当該項目に適用される css がアクティブなものに切り替わる

実際にブラウザに表示された画面を見ると、その時に表示しているページのURLにマッチしている項目の背景色がハイライトされています。つまり「href が現在の URL と一致するかどうかに基づいて active CSS クラスを切り替える」というのはこれを意味しているのではないかと思われます。

`href`の値と現在表示されているページのURLとのマッチング方法を指定するのが `Match` 属性です。Home の NavLink を見ると `Match="NavLinkMatch.All"` という属性値が指定されています。「[NavLink コンポーネント](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-3.1#navlink-component)」には次のような説明があります。
><NavLink> 要素の Match 属性に割り当てられる 2 つの NavLinkMatch オプションがあります。
> ・NavLinkMatch.All:NavLink は、現在の URL 全体に一致する場合にアクティブになります。
> ・NavLinkMatch.Prefix (既定値):NavLink は、現在の URL の任意のプレフィックスに一致する場合にアクティブになります。

つまり、`NavLinkMatch.All` の場合は指定のパスに完全一致した場合にアクティブに切り替わり、`Match` を省略または `NavLinkMatch.Prefix` に設定すると、`href`値がURLの先頭部分に一致した場合にアクティブになる、ということです。

Home は `href=""` なので、もしこれが `NavLinkMatch.Prefix` だったら任意のURLにマッチしてしまいます。なのでここは `NavLinkMatch.All` が指定されているわけですね。では `Prefix` のほうの動きも確認してみましょう。

_Counter.razor_ の `@page` の部分を次のように修正します。

```html:Counter.razor
@page "/fetchdata/counter"
```
_NavMenu.razor_ の Counter の NavLink 部分を次のように修正します。

```html:NavMenu.razor/カウンターページへのリンク
            <NavLink class="nav-link" href="fetchdata/counter">
```
つまり、 Counter と Fetch data が同じプレフィックス `fetchdata` を持つようにしたわけです。修正後のイメージは、それぞれ次のようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/71a55bbf-51d2-626e-12e7-bf666e342793.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/c26c194d-fab9-50eb-02fb-592d8ea87bb1.png)

実行してみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/d2f97734-bbce-a11e-038a-22678c7c1576.png)
「Counter」をクリックした画面ですが、思ったとおり、「Counter」と「Fetch data」の両方がハイライトされていますね！

最後はコードブロックです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/75930147-4a4b-bc8f-30c3-f040f5786809.png)
`NavMenuCssClass`プロパティと`ToggleNavMenu()`メソッドが定義されています。`NavMenuCssClass` は `<div class="@NavMenuCssClass" >` という使われ方をしているので、どうやら class を `collapse` にするか、あるいは class 属性そのものを省略する[^null_attr]か、ということを切り替えているようです。`collapse` というのは _bootstrap_ という CSS セットで定義されているようなのですが、自分の環境だと見た目の変化は分かりませんでした。

[^null_attr]: Razer Pages で `<input type="checkbox" checked="@value">` のような記述をすると、`value` が _false_ や _null_ だった場合は、属性そのものが省略される仕組みになっています。また _true_ の場合は属性名のみが残ります。参考：[Learn Razor Pages](https://www.learnrazorpages.com/razor-pages/forms/checkboxes)

## _Program.cs_ と _Startup.cs_
最後の鬼門です。

_Program.cs_ は `Program` クラスを定義し、_Startup.cs_ は `Startup` クラスを定義しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/b996e437-7abb-04e0-8dd5-35b9563984d8.png)
`Program` クラスにはプログラムのエントリポイントである `Main()` が定義されています。`Main`からは `CreateHostBuilder` が呼ばれ、そこで `Startup` クラスが使われています。

`Startup` クラスには `ConfigureService()` と `Configure()` の2つのメソッドがあります。

まず `ConfigureService()`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/f876cb6f-3f74-a51f-5431-26b8c3d5dd11.png)
ここでは以下のことを行っているようです。

- Razor Pages の有効化 (`service.AddRazorPages()`)
- サーバサイド Blazor の有効化 (`services.AddServerSideBlazor()`)
- `WeatherForecastService` クラスのインスタンスをDI可能なシングルトンとして追加する (`services.AddSingleton<WeatherForecastService>()`)

サーバサイド[^blazorserver] Blazor の場合、はじめの2つは必ず呼ばれることになります。`AddSingleton<T>()` はアプリ全体で使い回したいシングルトンなインスタンスが必要となる場合に呼び出すことになります。

[^blazorserver]: 他に WebAssembly を用いたクライアント側の Blazor もあります。

次は `Configure()`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/121763/84a5bf12-c90f-953e-c8a9-aafba90d5605.png)
`Configure()` には `Use～` というメソッド呼び出しが並んでいます。プログラム中のコメントにも書いてありますが、これはいわゆる [HTTP パイプライン](https://ja.wikipedia.org/wiki/HTTP%E3%83%91%E3%82%A4%E3%83%97%E3%83%A9%E3%82%A4%E3%83%B3)フェーズで行われる処理を並べたもののようです。

`add.UseRouting()` でルーティング機構を有効にしています。`endpoints.MapBlazorHub()` と `endpoints.MapFallbackToPage()` については「[ASP.NET Core エンドポイントのルーティングの統合](https://docs.microsoft.com/ja-jp/aspnet/core/blazor/fundamentals/routing?view=aspnetcore-3.1#aspnet-core-endpoint-routing-integration)」に説明があります。

>Blazor Server は ASP.NET Core エンドポイントのルーティングに統合されています。 ASP.NET Core アプリは、Startup.Configure で MapBlazorHub を使用して、対話型コンポーネントの着信接続を受け入れるように構成します。
[略]
>最も一般的な構成は、すべての要求を Razor ページにルーティングすることです。これは、Blazor Server アプリのサーバー側部分のホストとして機能します。 通常、ホスト ページは、_Host.cshtml という名前になります。

`MapBlazorHub()` の呼び出しにより、ブラウザ側からの通信を受け付けるようになるということかと思われます。`MapFallbackToPage("/_Host")` の呼び出しは、フォールバックページを _\_Host_ に設定するということでしょう。

さて次回以降、このサンプルプロジェクトを換骨奪胎して、我々独自のアプリに作りかえていくことにします。