Blazor WebAssembly を使ってみた

2020年5月の「Microsoft Buld 2020」で、「Blazor WebAssembly」の正式リリースが発表されました。Blazor WebAssemblyは、C#を用いて、Webブラウザ上で実行可能なWebアプリケーションを開発できるフレームワークです。
Webフロントエンドシステムは、JavaScript系フレームワークが流行っていますが、JavaScriptを書かなくても、SPAアプリケーションが作成できます。
ということで、早速使ってみることにしました。
前回の記事 では、ASP.NET Coreを使った簡単なREST API(以下、従業者管理APIと呼びます)を作りましたので、Blazor WebAssemblyで作ったアプリから、そのAPIを呼び出してみます。
『新しいプロジェクトの作成』画面で『Blazor アプリ』を選択し、次へ進みます。

続けて、プロジェクト名、ソリューション名に任意の名前を入力し、プロジェクトの作成先を選択します。
今回は以下の内容で作成しました。
『新しい Blazor アプリを作成します』画面でプロジェクトのテンプレートを選択します。
今回は、Blazor WebAssemblyを作成したいので『Blazor WebAssembly App』を選択します。
また、ASP.NET Coreベースとするため、ASP.NET Core hosted をチェックします。

ソリューションエクスプローラーに画像のようなプロジェクトができていればプロジェクトの作成まで完了です!
プロジェクトは、「BlazorTest.Client」「BlazorTest.Server」「BlazorTest.Shared」の3つが作成されます。

作成されたプロジェクトには、サンプルアプリが含まれていますので、そのままビルドして実行してみます。
実行すると、以下のような画面が表示されました。

以下のメニューがありますので、適当に操作してみてください。
「Home」:Hello, worldが表示されたページ
「Counter」:ボタンを押すとカウントアップするページ
「Fetch data」:天気予報API(WeatherForecast)を呼び出して、取得結果を表示するページ
サンプルアプリの説明は割愛します。興味がある方はソースコードをご参照ください。
追加するページは、前回作成した従業者管理APIを使い、BlazorアプリからのCRUD処理を実装してみます。
※従業者管理APIについては、前回の記事 を参照
最初に BlazorTest.Shared プロジェクトに、Employeeクラスを作成します。
■Employee.cs
次に、BlazorTest.Client の Pages フォルダを右クリックし、「追加」→「Razorコンポーネント」を選択します。
ファイル名を「EmployeeData.razor」として、「追加」ボタンをクリックします。
作成されたファイルのコードを以下のようにします。
「EmployeeAPIEndPoint」の部分は、従業者管理APIのエンドポイントを設定してください。
■EmployeeData.razor
作成したページをメニューから呼び出せるようにします。
BlazorTest.Client プロジェクトの「Shared」フォルダにある、NavMenu.razor を開き、以下の内容に変更します。
■NavMenu.razor
追加したページを表示する準備が整いました。
実行してみましょう。
※従業者管理APIを実行できる状態にしておいてください。
「従業者管理」メニューが追加されました。

次に、「従業者管理」メニューをクリックし、ページを表示してみます。

エラーになってしまいました。
サンプル実装されている Fetch data 画面では、WeatherForecastを呼び出して、取得結果を表示できているのに何故!?
ChromeのDevToolsで確認してみます。Shift + Alt + D で、DevToolsを起動できます。
DevToolsのコンソールを確認してみると、赤字で以下のメッセージが出力されていました。

---
Access to fetch at 'https://localhost:44326/api/Employees' from origin 'https://localhost:44366' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
---
CORS(Cross-Origin Resource Sharing) policy でエラーになっていたことを確認できます。
Blazor WebAssemblyのサンプルで実装されている WeatherForecast は、自身のサイトにあるAPIのため、CORS問題は発生しませんでしたが、今回呼び出している従業者APIは、同じlocalhostではありますが、異なるポートで動いているAPIのため、別オリジンとなります。
このため、そのままでは呼び出せません。
ASP.NET Coreでの設定方法は、いくつかありますので、詳細は以下のページをご参照ください。
https://docs.microsoft.com/ja-jp/aspnet/core/security/cors?view=aspnetcore-3.1
従業者管理API側の「Startup.cs」を以下の通り修正し、ビルドします。
■Startup.cs(一部抜粋)
WithOrigins部分に、アクセス元オリジンを記載します。
※アクセス元オリジンは、環境に合わせて修正してください。
再び、BlazorTestアプリの「従業者管理」をクリックすると、登録済の従業者一覧が表示できました。

それらの機能を、今回作成したアプリから呼び出してみます。
※簡易的なアプリの為、エラーチェック等の実装は行っておりません。
■EmployeeData.razor

追加、編集、削除機能の実装が完了しました。
追加するときは、一番上の入力欄に入力し、「追加」ボタンをクリックします。
更新するときは、「編集」ボタンをクリックすると、その行が編集モードになり、更新できます。
「削除」ボタンを押すと、その行のデータを削除できます。
次回以降、複数のAPIを連携させたり、拡張させていきたいと思っています。
QESでは様々なアプリケーションの開発・導入を行っております。
私共が提供するサービス・ソリューションにつきましてはこちらに掲載しております。
また、現在Power Platform に力を入れて取り組んでいます。Power Apps等のPower Platformについては、こちらをご覧ください。
システム開発・構築でお困りの問題や弊社が提供するサービス・ソリューションにご興味を抱かれましたら、是非一度お問い合わせください。
また新しい技術に取り組んでいるQESでは
クラウドエンジニアを募集しています。
仕事内容や転職にご興味のある方はこちらもご覧ください。

このブログで参照されている、Microsoft、 Windows、PowerApps、その他のマイクロソフト製品およびサービスは、 米国およびその他の国におけるマイクロソフトの商標または登録商 標です。
Webフロントエンドシステムは、JavaScript系フレームワークが流行っていますが、JavaScriptを書かなくても、SPAアプリケーションが作成できます。
ということで、早速使ってみることにしました。
前回の記事 では、ASP.NET Coreを使った簡単なREST API(以下、従業者管理APIと呼びます)を作りましたので、Blazor WebAssemblyで作ったアプリから、そのAPIを呼び出してみます。
1. 環境
本記事では、以下の環境を使用しています。- OS:Windows 10
- IDE:Visual Studio 2019
- データベース:SQL Server 2016 Express LocalDB
- プログラミング言語:C#
- アプリケーション基盤:ASP.NET Core 3.1、Blazor WebAssembly 3.2.0
2. プロジェクト作成
はじめに、Visual Studio 2019を開き、『新しいプロジェクトの作成』をクリックします。『新しいプロジェクトの作成』画面で『Blazor アプリ』を選択し、次へ進みます。
続けて、プロジェクト名、ソリューション名に任意の名前を入力し、プロジェクトの作成先を選択します。
今回は以下の内容で作成しました。
- ソリューション名:『BlazorTest』
- プロジェクト名:『BlazorTest』
- 場所(プロジェクトの作成先):任意の場所
『新しい Blazor アプリを作成します』画面でプロジェクトのテンプレートを選択します。
今回は、Blazor WebAssemblyを作成したいので『Blazor WebAssembly App』を選択します。
また、ASP.NET Coreベースとするため、ASP.NET Core hosted をチェックします。
ソリューションエクスプローラーに画像のようなプロジェクトができていればプロジェクトの作成まで完了です!
プロジェクトは、「BlazorTest.Client」「BlazorTest.Server」「BlazorTest.Shared」の3つが作成されます。
作成されたプロジェクトには、サンプルアプリが含まれていますので、そのままビルドして実行してみます。
実行すると、以下のような画面が表示されました。
以下のメニューがありますので、適当に操作してみてください。
「Home」:Hello, worldが表示されたページ
「Counter」:ボタンを押すとカウントアップするページ
「Fetch data」:天気予報API(WeatherForecast)を呼び出して、取得結果を表示するページ
サンプルアプリの説明は割愛します。興味がある方はソースコードをご参照ください。
3. 独自ページの作成
サンプル実装を残したまま、新しいページを追加してみます。追加するページは、前回作成した従業者管理APIを使い、BlazorアプリからのCRUD処理を実装してみます。
※従業者管理APIについては、前回の記事 を参照
最初に BlazorTest.Shared プロジェクトに、Employeeクラスを作成します。
■Employee.cs
using System; namespace BlazorTest.Shared { public class Employee { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } public DateTime HireDate { get; set; } = DateTime.Now; } }
次に、BlazorTest.Client の Pages フォルダを右クリックし、「追加」→「Razorコンポーネント」を選択します。
ファイル名を「EmployeeData.razor」として、「追加」ボタンをクリックします。
作成されたファイルのコードを以下のようにします。
「EmployeeAPIEndPoint」の部分は、従業者管理APIのエンドポイントを設定してください。
■EmployeeData.razor
@page "/employeedata" @using BlazorTest.Shared @inject HttpClient Http <h1>従業者管理</h1> <p> 従業者を管理します。 </p> @if (employees == null) { <p><em>読み込み中</em></p> } else { <table class="table"> </table> <br /> <table class="table"> <thead> <tr> <th>従業者番号</th> <th>氏名</th> <th>年齢</th> <th>入社日</th> </tr> </thead> <tbody> @foreach (var employee in employees) { <tr> <td>@employee.Id</td> <td>@employee.Name</td> <td>@employee.Age</td> <td>@employee.HireDate.ToShortDateString()</td> </tr> } </tbody> </table> } @code { private const string EmployeeAPIEndPoint = "https://localhost:44326/api/Employees"; private Employee[] employees; protected override async Task OnInitializedAsync() { employees = await Http.GetFromJsonAsync<Employee[]>(EmployeeAPIEndPoint); } }
作成したページをメニューから呼び出せるようにします。
BlazorTest.Client プロジェクトの「Shared」フォルダにある、NavMenu.razor を開き、以下の内容に変更します。
■NavMenu.razor
<div class="top-row pl-4 navbar navbar-dark"> <a class="navbar-brand" href="">BlazorTest</a> <button class="navbar-toggler" @onclick="ToggleNavMenu"> <span class="navbar-toggler-icon"></span> </button> </div> <div class="@NavMenuCssClass" @onclick="ToggleNavMenu"> <ul class="nav flex-column"> <li class="nav-item px-3"> <NavLink class="nav-link" href="" Match="NavLinkMatch.All"> <span class="oi oi-home" aria-hidden="true"></span> Home </NavLink> </li> <li class="nav-item px-3"> <NavLink class="nav-link" href="counter"> <span class="oi oi-plus" aria-hidden="true"></span> Counter </NavLink> </li> <li class="nav-item px-3"> <NavLink class="nav-link" href="fetchdata"> <span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data </NavLink> </li> <li class="nav-item px-3"> <NavLink class="nav-link" href="employeedata"> <span class="oi oi-list-rich" aria-hidden="true"></span> 従業者管理 </NavLink> </li> </ul> </div> @code { private bool collapseNavMenu = true; private string NavMenuCssClass => collapseNavMenu ? "collapse" : null; private void ToggleNavMenu() { collapseNavMenu = !collapseNavMenu; } }
追加したページを表示する準備が整いました。
実行してみましょう。
※従業者管理APIを実行できる状態にしておいてください。
「従業者管理」メニューが追加されました。
次に、「従業者管理」メニューをクリックし、ページを表示してみます。
エラーになってしまいました。
サンプル実装されている Fetch data 画面では、WeatherForecastを呼び出して、取得結果を表示できているのに何故!?
ChromeのDevToolsで確認してみます。Shift + Alt + D で、DevToolsを起動できます。
DevToolsのコンソールを確認してみると、赤字で以下のメッセージが出力されていました。
---
Access to fetch at 'https://localhost:44326/api/Employees' from origin 'https://localhost:44366' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
---
CORS(Cross-Origin Resource Sharing) policy でエラーになっていたことを確認できます。
Blazor WebAssemblyのサンプルで実装されている WeatherForecast は、自身のサイトにあるAPIのため、CORS問題は発生しませんでしたが、今回呼び出している従業者APIは、同じlocalhostではありますが、異なるポートで動いているAPIのため、別オリジンとなります。
このため、そのままでは呼び出せません。
4. CORS対策
エラーメッセージに出力されている通り、今回作成しているアプリは「https://localhost:44366」から、従業者管理APIにアクセスしているため、アクセスを許可してあげる必要があります。ASP.NET Coreでの設定方法は、いくつかありますので、詳細は以下のページをご参照ください。
https://docs.microsoft.com/ja-jp/aspnet/core/security/cors?view=aspnetcore-3.1
従業者管理API側の「Startup.cs」を以下の通り修正し、ビルドします。
■Startup.cs(一部抜粋)
public void ConfigureServices(IServiceCollection services) { services.AddDbContext(opt => opt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); // CORS対策 ここから services.AddCors(options => { options.AddDefaultPolicy( builder => { builder.WithOrigins("https://localhost:44366") .AllowAnyMethod() .AllowAnyHeader(); }); }); // CORS対策 ここまで services.AddControllers(); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseHttpsRedirection(); app.UseRouting(); // CORS対策 ここから app.UseCors(); // CORS対策 ここまで app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); }
WithOrigins部分に、アクセス元オリジンを記載します。
※アクセス元オリジンは、環境に合わせて修正してください。
再び、BlazorTestアプリの「従業者管理」をクリックすると、登録済の従業者一覧が表示できました。
5. 追加、変更、削除機能の実装
従業者管理APIには、新規登録、変更、削除機能があります。それらの機能を、今回作成したアプリから呼び出してみます。
※簡易的なアプリの為、エラーチェック等の実装は行っておりません。
■EmployeeData.razor
@page "/employeedata" @using BlazorTest.Shared @inject HttpClient Http <h1>従業者管理</h1> <p> 従業者を管理します。 </p> @if (employees == null) { <p><em>読み込み中</em></p> } else { <table class="table"> </table> <br /> <table class="table"> <thead> <tr> <th>従業者番号</th> <th>氏名</th> <th>年齢</th> <th>入社日</th> </tr> </thead> <tbody> <tr> <td></td> <td><input type="text" @bind="addEmployee.Name" /></td> <td><input type="number" @bind="addEmployee.Age" /></td> <td><input type="date" @bind="addEmployee.HireDate" /></td> <td><button @onclick="Add">追加</button></td> </tr> @foreach (var employee in employees) { <tr> <td>@employee.Id</td> @if (editEmployee.Id == employee.Id) { <td><input type="text" @bind="editEmployee.Name" /></td> <td><input type="number" @bind="editEmployee.Age" /></td> <td><input type="date" @bind="editEmployee.HireDate" /></td> <td><button @onclick="() => Update(editEmployee.Id)">更新</button>
<button @onclick="() => Cancel()">キャンセル</button></td> } else { <td>@employee.Name</td> <td>@employee.Age</td> <td>@employee.HireDate.ToShortDateString()</td> <td><button @onclick="() => Edit(employee)">編集</button></td> } <td><button @onclick="() => Delete(employee.Id)">削除</button></td> </tr> } </tbody> </table> } @code { private const string EmployeeEndPoint = "https://localhost:44326/api/Employees"; private Employee[] employees; private Employee addEmployee = new Employee(); private Employee editEmployee = new Employee(); protected override async Task OnInitializedAsync() { await GetEmployees(); } private async Task GetEmployees() { employees = await Http.GetFromJsonAsync<Employee[]>(EmployeeEndPoint); } private async Task Add() { await Http.PostAsJsonAsync<Employee>(EmployeeEndPoint, addEmployee); addEmployee = new Employee(); await GetEmployees(); } private async Task Delete(int id) { await Http.DeleteAsync($"{EmployeeEndPoint}/{id}"); await GetEmployees(); } private async Task Update(int id) { await Http.PutAsJsonAsync($"{EmployeeEndPoint}/{id}", editEmployee); await GetEmployees(); editEmployee.Id = -1; } private void Edit(Employee employee) { editEmployee = employee; } private async Task Cancel() { await GetEmployees(); editEmployee.Id = -1; } }
追加、編集、削除機能の実装が完了しました。
追加するときは、一番上の入力欄に入力し、「追加」ボタンをクリックします。
更新するときは、「編集」ボタンをクリックすると、その行が編集モードになり、更新できます。
「削除」ボタンを押すと、その行のデータを削除できます。
6. 最後に
今回は、WebAssemblyを使用した簡単なフロントエンドアプリを作成してみました。次回以降、複数のAPIを連携させたり、拡張させていきたいと思っています。
QESでは様々なアプリケーションの開発・導入を行っております。
私共が提供するサービス・ソリューションにつきましてはこちらに掲載しております。
また、現在Power Platform に力を入れて取り組んでいます。Power Apps等のPower Platformについては、こちらをご覧ください。
システム開発・構築でお困りの問題や弊社が提供するサービス・ソリューションにご興味を抱かれましたら、是非一度お問い合わせください。
また新しい技術に取り組んでいるQESでは
クラウドエンジニアを募集しています。
仕事内容や転職にご興味のある方はこちらもご覧ください。
このブログで参照されている、Microsoft、