BlazorServerプロジェクトの構成を理解する
ここで、BlazorServerSampleプロジェクトがどのような構成になっているか、VSCodeのエクスプローラーで見てみましょう(図6)。
プロジェクトの構成
プロジェクトには、Data、Pages、Shared、wwwrootなどのフォルダと、_Imports.razor、App.razor、Program.csなどのファイルがあります。wwwrootフォルダはRazor Pagesと共通ですので、第2回の該当箇所を参照してください。
Pagesフォルダ
Blazorでは、ページを構成する要素はPagesフォルダに集約されています。Pagesフォルダには、Error.cshtml(.cs)といったRazorページと、Index.razorといったRazorコンポーネントの2種類のファイルが格納されます。
Razorページは、Razor Pagesと同様にアプリケーションの各ページに対応するファイルです。このうち_Host.cshtmlと_Layout.cshtmlは、ファイル名がアンダースコア( _ )で始まることからもわかるように、特別な役割を持っています。前者は、フォールバックのためのページ、後者は各ページで共有するレイアウトです。
Razorコンポーネントも、アプリケーションの各ページに対応するファイルですが、ページ遷移を伴わないBlazorでは、ページの一部を構成する部品という位置付けになっています。Razorコンポーネントは、それぞれがURLのパス情報を持ち、要求されたURLに対応するRazorコンポーネントでページを書き換えるといった処理が行われます。
[NOTE]RazorページはBlazor WebAssemblyには存在しない
2つのRazorページはBlazor WebAssemblyには存在しません。Blazor WebAssemblyでは、たとえば共有レイアウトはwwwroot/index.htmlファイルが受け持っています。
Sharedフォルダ
Sharedフォルダには、各ページで共有するRazorコンポーネントが置かれています。デフォルトレイアウトのためのMainLayout.razor、ナビゲーションメニューのためのNavMenu.razor、そしてindex.razorで読み込むコンポーネントであるSurveyPrompt.razorが既定で置かれます。
Dataフォルダ
Dataフォルダは、データソースのためのフォルダです。データの入れ物としてのPOCO(WeatherForecast.cs)と、サービスの処理が書かれたクラスファイル(WeatherForecastService.cs)が置かれています。
Razorコンポーネントを見る
Razorコンポーネントについて、具体的に見てみます。Razorコンポーネントでは、Razorページと同様にRazor構文を用いて内容を記述します。以下のリストは、サイドバーで[Counter]をクリックした際に呼び出されるCounterコンポーネントの内容です。
@page "/counter" (1) <PageTitle>Counter</PageTitle> (2) <h1>Counter</h1> <p role="status">Current count: @currentCount</p> (3) <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> (4) @code { (5) private int currentCount = 0; private void IncrementCount() { currentCount++; } }
(1)は@pageディレクティブですが、Razorページと異なり値がパスとなっています。これは、Blazorにおけるルーティングの指定で、/counterがURLに指定された場合、このコンポーネントを呼び出すことを指定しています。(2)はページのタイトルの指定です。コンポーネントの呼び出しで、このタイトルでページのタイトルを書き換えます。
(3)はモデル変数currentCountを参照してp要素で表示しています。(4)はボタンの指定ですが、onclick属性の指定でクリック時にIncrementCountメソッドを呼び出すことを指定しています。@onclickとなっているように、サーバーサイドの指定であることに注意してください。
(5)以降は、@codeディレクティブで囲まれたコードブロックです。変数currentCountを初期化し、IncrementCountメソッドを実装しています。
クライアントサイドで、JavaScriptを使って同様のコードを書けますが、ここではC#でサーバーサイドのコードを書いていることに注目してください。これなら、C#でサーバーサイドの開発経験があれば、無理なく入っていけることを理解していただけるのではないでしょうか?
データソースを見る
FetchDataコンポーネント(Pages/FetchData.razorファイル)も上記と同様の構造です。ただし、こちらは表示するデータを外部から取得している点が異なります。以下のリストは、FetchData.razorでデータを取得している部分の抜粋です。
private WeatherForecast[]? forecasts; protected override async Task OnInitializedAsync() { forecasts = await ForecastService.GetForecastAsync(DateTime.Now); }
OnInitializedAsyncという初期化メソッドの呼び出しにおいて、ForecastService.GetForecastAsyncメソッドを呼び出して、データを取得しています。このメソッドがどこで定義されているかというと、データソースであるData/WeatherForecastService.csファイルです。
public class WeatherForecastService { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" (1) public Task<WeatherForecast[]> GetForecastAsync(DateTime startDate) (2) { return Task.FromResult(Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = startDate.AddDays(index), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }).ToArray()); } }
(1)は天気の概要の文字列です。(2)が、ForecastService.GetForecastAsyncメソッドの定義で、日付、乱数からなる気温、同じく乱数からなる天気の概要という、5個の要素を持つ配列を作成して返しています。この配列の内容を、Pages/FetchData.razorでは表示しているというわけです。
[NOTE]Blazor WebAssemblyではDataフォルダは存在しない
Blazor WebAssemblyでは、データは外部サービスから取得するという前提です。そのため、Blazor WebAssemblyアプリケーションの既定では静的に用意されたダミーのデータを読み込んで表示するだけ、という動作になっています。
App.razor
App.razorは、ルーティングのためのファイルです。ルーティング先があるときとないときの処理が既定で記述されています。
<Router AppAssembly="@typeof(App).Assembly"> <Found Context="routeData"> (1) <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> <FocusOnNavigate RouteData="@routeData" Selector="h1" /> </Found> <NotFound> (2) <PageTitle>Not found</PageTitle> <LayoutView Layout="@typeof(MainLayout)"> <p role="alert">Sorry, there's nothing at this address.</p> </LayoutView> </NotFound> </Router>
指定されたパスに一致するコンポーネントがあれば、(1)の記述に従ってレイアウトにMainLayoutコンポーネントを使用してレンダリングして表示します。一致するコンポーネントがない場合には、(2)の記述に従ってレイアウトにMainLayoutを使用した上でエラーメッセージ「Sorry, there's nothing at this address.」を表示します。MainLayoutは、Shared/MainLayout.cshtmlに記述されるレイアウトを意味します。
Program.cs
最後のProgram.csは、アプリケーションの基点となるファイルです。Razor Pages、MVCと同様に、アプリケーションの起動ロジックを受け持ちます。リストはその抜粋です。
…略… builder.Services.AddSingleton<WeatherForecastService>(); (1) …略… app.MapFallbackToPage("/_Host"); (2) app.Run();
(1)は、Pages/FetchData.razorで使用されていたWeatherForecastServiceクラスを、シングルトンオブジェクトとして生成しています。(2)は、Pages/_Host.cshtmlをフォールバックページとしてマップしています。
Blazorは、とにかく関連するファイルの種類や数が多いのですが、①App.razorでルーティングが合致した場合のレイアウト(MainLayoutコンポーネント)を指定、②MainLayoutコンポーネントでルートに対応するRazorコンポーネントを読み込み、③最終的なレンダリングは_layout.cshtmlに基づいて行う、という流れを理解しておけばよいでしょう。
まとめ
今回は、対話型のWeb UI開発のためのフレームワークであるBlazorを、Blazor Serverを中心に見てきました。次回は、Blazorのバックエンドとも言えるWeb APIの開発を見ていきます。