🎉

.Net8のBlazor WASMでASP.Net Core Hostedの構成を作る方法

2023/12/02に公開

はじめに

.Net8が公開され、Blazorも今までと大きな変更が来ました。
.Net7まではwasmオンリーのテンプレートならASP.Net Core Hostedで簡単にAPIとフロントエンドをまとめて作ることができましたが、なぜか.Net8ではASP.Net Core Hostedの選択肢が無くなり、テンプレートから作れなくなりました。
そこで、.Net8でも同じような構成で作りたいなーって思ったので.Net7のプロジェクトを参考に同じような構成を作る手順をまとめていきます。

構成

今回のプロジェクト作成には以下の構成で作成しました。

  • Windows11
  • Visual Studio 2022
  • .Net8

手順の概要

  1. Blazorプロジェクトの作成
  2. ASP.Net Core Web APIプロジェクトの作成
  3. クラスライブラリ(Share)プロジェクト作成
  4. ASP.Net Core Web APIプロジェクトの修正
  5. Blazorの接続先をWeb APIにするように修正

1.Blazorプロジェクトの作成

まずはBlazorプロジェクトの作成をします。
.Net8で新規追加された「Blazor Web App」ではなく「Blazor WebAssemblyアプリ」のプロジェクトで作成していきます。
Blazor WebAssemblyプロジェクト

プロジェクト名は何でもいいですが、「~.Client」プロジェクトにすると何のプロジェクトか分かりやすいのでおすすめです。(ソリューション名は~までにした方がいいと思います。)
プロジェクト名入力

フレームワークは.Net8にしましょう。
.Net7だと「ASP.Net Core Hosted」のチェックを入れることができますが、.Net8は無いです。残念。
HTTPS用の構成だけ入れたらあとは自由に設定していただければと思います。
フレームワーク設定

クライアントのプロジェクトが完了しました。
クライアントプロジェクト作成

2.ASP.Net Core Web APIプロジェクトの作成

Blazorとやり取りするためのバックエンド兼、Webサーバーになるプロジェクトの作成を実施します。
まずはソリューションを右クリックし、「追加」>「新しいプロジェクト」を選択します。
プロジェクト追加

プロジェクトのテンプレートから「ASP.Net Core Web API」を選択します。
WebAPIプロジェクトテンプレート

プロジェクト名はこちらもなんでもいいと思いますが、「{ソリューション名}.Server」にするとわかりやすいのでお勧めです。
WebAPIプロジェクト名入力

フレームワークはBlazorと同様に.Net8を選択しましょう。
Blazorのメリットとして、サーバーとのやり取りをC#で共通でかけることがあるので、なるべくフレームワークも合わせて共通のクラスを作って楽をしましょう。
それ以外は度の設定でもいいですが、「HTTPS用の構成」はチェックを入れておいた方がいいと思います。
WebAPI追加情報

3.クラスライブラリ(Share)プロジェクト作成

せっかくのBlazorアプリなので共通クラスを作成しましょう。
流れとしては上と同じでクラスライブラリプロジェクトを作成する+それぞれのプロジェクトで参照します。

VSでソリューションを右クリックし、「追加」>「新しいプロジェクト」を選択し、表示されたウィンドウで「クラスライブラリ」を選択します。
クラスライブラリ

プロジェクト名は他のと同じで何でもいいですが、今回は「{ソリューション名}.Share」にします。
1つしかプロジェクト作らないのでShareにしてますが、複数の共通プロジェクト作るときはそれぞれで分かりやすい命名にすることをお勧めします。(DBとかOpenAIなどなど)
クラスライブラリプロジェクト名

フレームワークは他のと同様に.Net8を選択します。
クラスライブラリフレームワーク選択

クラスライブラリのプロジェクトが完成したので、ClientとServerのそれぞれで参照できるように設定していきます。
Clientプロジェクト内の「依存関係」を右クリックし、「プロジェクト参照の追加」を選択します。
プロジェクト参照の追加

新しく出てきたウィンドウでShareプロジェクトにチェックを入れ、「OK」を選択します。
ClientでShareプロジェクトの参照
依存関係を展開し、プロジェクト内にShareプロジェクトが追加されたことを確認します。
Client参照プロジェクトの確認

Serverプロジェクトでも同様の手順を行い、依存関係に追加されることを確認します。
Server参照プロジェクトの確認

これでShareプロジェクトの準備が完了しました。

4.ASP.Net Core Web APIプロジェクトの修正

ここからがASP.Net Core Hostedを行うための手順になります。
大まかな流れとしては以下の通りです。

  1. 必要なパッケージの追加
  2. Serverプロジェクトの依存関係にClientプロジェクトを追加
  3. Program.csの修正
  4. launchSettings.jsonを修正

1.必要なパッケージの追加

それではまずは必要なパッケージを追加します。
Serverプロジェクトで「依存関係」を右クリックし、「NuGetパッケージの管理」を選択します。
NuGetパッケージの管理
参照タブを選択し、検索窓で「Microsoft.AspNetCore.Components.WebAssembly.Server」を入力し、インストールします。
NuGetパッケージインストール後

2.Serverプロジェクトの依存関係にClientプロジェクトを追加

Serverプロジェクトの「依存関係」を右クリックし「プロジェクト参照の追加」を選択し、Clientプロジェクトにもチェックをいれます。
Clientプロジェクトの追加

3.Program.csの修正

Program.csを以下のように修正します。

// Add services to the container.

- builder.Services.AddControllers();
+ builder.Services.AddControllersWithViews();
+ builder.Services.AddRazorPages();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
+   app.UseWebAssemblyDebugging();
}

app.UseHttpsRedirection();


+ app.UseBlazorFrameworkFiles();
+ app.UseStaticFiles();
+ app.MapRazorPages();
app.MapControllers();
+ app.MapFallbackToFile("index.html");

以下が全体コードになります。

Program.cs
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
    app.UseWebAssemblyDebugging();
}

app.UseHttpsRedirection();

app.UseBlazorFrameworkFiles();
app.UseStaticFiles();

app.UseAuthorization();

app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");

app.Run();

4.launchSettings.jsonを修正

一応ASP.Net Core hosted環境で動かせる設定になりましたが、このままServerプロジェクトを実行するとSwaggerの画面が起動します。
いちいちBlazorの画面に遷移するのはめんどくさいので、launchSettings.jsonを修正してBlazor画面を出せるようにしていきます。

ServerプロジェクトのPropertiesフォルダのlaunchSettings.jsonを開き、「Ctrl+f」で検索窓を開き、置換できるようにします。
検索に"launchUrl": "swagger",を入力、置換に"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",を入力し、すべて置換していきます。
launchSettings.jsonの置換

実際に動かしてみる

それではServer側で必要な設定が完了したので、実際に動かしてみましょう。
実行対象のプロジェクトをServerに変更し、f5で実行しましょう。
実行結果

Serverプロジェクトを実行したにもかかわらず、Blazorアプリが起動しました。
これだけだと本当にClientプロジェクトのやつかわからないので、実際に編集してみましょう。

まずは画面上部の炎アイコンの右の下矢印をクリックし、「ファイル保存時のホットリロード」にチェックが入っているか確認しましょう。
チェックがない場合は選択します。
ホットリロード

これで再ビルドせずに画面の変更を即時反映できるようになりました。

Clientプロジェクトの「Pages」フォルダの「Home.razor」を開きます。
「Wellcome to your new app」を「ようこそ!」に変更して保存しましょう。

@page "/"

<PageTitle>Home</PageTitle>

<h1>Hello, world!</h1>

- Welcome to your new app.
+ ようこそ!

保存して少し待つか、うまくいかないときは画面をリロードしましょう。
そうすると表示される文字列が変わるので、ちゃんとClientプロジェクトのプログラムを参照していることが分かります。
変更後

Blazorの接続先をWeb APIにするように修正

これでASP.Net Core Hostedの構成になりましたが、今のところただのBlazorの画面を配送するWebサーバーの役割でしかないので、APIでサーバーとクライアントが通信できるように修正します。
Blazor自体はProgram.csの設定でHttpClientのベースアドレスをサーバーのベースアドレスにするように設定しているので、APIのルーティングを指定するだけで取得できるようになります。

それではWeb APIを叩きに行きましょう。

まずはClientプロジェクトの「Pages」>「Weather.razor」を選択します。
現在はjsonファイルをHttpClientで取得するプログラムになっていますので、これをWeb APIプロジェクトのWeatherForecastから録ってこれるようにします。

@code内の処理を以下のように修正します。

@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
-        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json");
+        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("weatherforecast");
    }

    public class WeatherForecast
    {
        public DateOnly Date { get; set; }

        public int TemperatureC { get; set; }

        public string? Summary { get; set; }

        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
    }
}

保存してWeatherタブを開くと何の問題もなく動きました。
Weatherタブ

とはいえ、このままだとjsonファイルをそのまま参照してるかわからないので、Server側の処理を変えてわかりやすくしましょう。
Serverプロジェクトの「Contorollers」フォルダ内の「WeaterForecastController.cs」を開き、Getメソッドを変更します。

[HttpGet(Name = "GetWeatherForecast")]
public IEnumerable<WeatherForecast> Get()
{
-    return Enumerable.Range(1, 5).Select(index => new WeatherForecast
+    return Enumerable.Range(1, 100).Select(index => new WeatherForecast
    {
        Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
        TemperatureC = Random.Shared.Next(-20, 55),
        Summary = Summaries[Random.Shared.Next(Summaries.Length)]
    })
    .ToArray();
}

一回デバッグ実行を停止し、再度実行します。
取得できるデータ量がAPIの変更に合わせて増えたので、ちゃんとAPI経由で情報取得できることを確認しました。
API取得

これで完成!と言いたいところですが、今のところShareプロジェクトが使われていません。
今回の処理でAPIの受け渡しで同じクラスをServerとClientのそれぞれのプロジェクトで定義しています。
このやり方でも悪くないと思いますが、API側で定義を変更したらClient側でも同じように修正する必要が出てきてしまいます。
それだとせっかくすべてをC#で書けるBlazorのメリットが小さくなるので、クラスを共通化していきましょう。

Serverの「WeatherForecast.cs」ファイルをShareプロジェクトにコピペします。
WeatherForecast.csのコピペ

Serverの「WeatherForecast.cs」ファイルを削除し、Shareクラスの「WeatherForecast.cs」のnamespaceを修正します。

- namespace BlazorASPNetHostedTest.Server
+ namespace BlazorASPNetHostedTest.Share

そうするとエラーが出るので、ServerプロジェクトのControllerフォルダの「WeatherForecastController.cs」を開き、エラー出てるか所を開き、電球マークを開き「using ~.Share」を選択します。
usingの追加

するとファイルの1行目にusingが追加されてエラーが解消されたと思います。
これでServer側のクラスをShareプロジェクトから参照できるようになりました。

次はClient側の修正を行います。
Weather.razorを選択し、@code内のWeatherForecastクラスの定義を削除します。

public class WeatherForecast
@code {
    private WeatherForecast[]? forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("weatherforecast");
    }

-    public class WeatherForecast
-    {
-        public DateOnly Date { get; set; }
-        public int TemperatureC { get; set; }
-        public string? Summary { get; set; }
-        public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
-    }
}

そうするとこちらでもエラーが出るので、電球マークをクリックし「@using ~.Share」を選択し、Shareプロジェクト内のクラスを参照できるようにします。
using追加

エラーが解消されたら再度実行してエラーが出ないことを確認します。
Shareプロジェクト修正後

以上でShareプロジェクトから参照できるようになりました。
ようやく共通処理やクラスを1つで管理できるようになりましたので、Blazorとしての強みを生かした開発ができるようになりました。

おわりに

.Net8でBlazor wasmのASP.Net Core hostedのプロジェクトを作成することができました。
やったことはプロジェクト作ってNuGetパッケージ追加してちょろちょろっと修正した程度なので、覚えてしまえばなんてことないですが、やっぱりテンプレートでほしいですね。
MSとしてはBlazor Web Appのテンプレートを使ってほしいんでしょうかね。
いずれ試したいと思いつつ、公式ドキュメントの対応が間に合ってない感あるのでなかなか触りにくいんですよね。

今回のコードはGitHubで公開してますので、気になった方はそちらもご覧ください。

https://github.com/vigilanteYU/BlazorASPNetHostedTest

こちらのやり方は以下の動画を参考にさせていただきました。
英語分かりませんが、手順自体は.Net7のプロジェクトと同じような構成にする方法だったので何とかできました。
Blazor Wasm+APIの構成を手軽にやりたかったので感謝です。

https://youtu.be/3Ur79_kHVpo?si=MypRR2Tnbvi5Dr2D

Discussion