SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

.NET最新版でASP.NET Core

ASP.NET Coreの軽量Web APIフレームワーク「Minimal API」を使ったSPA開発

.NET最新版でASP.NET Core 第9回

SPAテンプレートを利用する

 第8回の冒頭で触れたように、Web APIはReactやAngularなどのSPA(Single Page Application)と組み合わせての利用が想定されます。これを受けて、ASP.NET CoreにはSPA利用のためのテンプレートも用意されており、それらを利用することでクライアントサイド(SPA)とサーバサイド(Web API)の双方を一度に生成して利用することができるようになっています。ここでは、このうちReactによるSPA開発を紹介します。

プロジェクトの作成と実行

 Reactをフロントエンドに用いる開発では、テンプレートにreactを指定してdotnet newコマンドを実行します。指定されているオプションは、第8回でWeb APIの生成に用いたものと同じです。

% dotnet new react -o ReactApp --no-https -f net7.0

 なお、これまで同様にdotnet new --listコマンドでの出力を見ると、reactテンプレートは「react」は「React.js での ASP.NET Core」に相当し、言語はC#で、タグには「Web/MVC/SPA」が指定されています。これで、クライアントサイドとサーバサイドが一度に生成されます。双方が、一つのdotnet runコマンドあるいはdotnet watchコマンドで実行できます。

% dotnet watch
dotnet watch 🔥 Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload. 
  💡 Press "Ctrl + R" to restart.
dotnet watch 🔧 Building...
…略…

 これで、以下の図3のページが最初に表示され、しばらく経つと本来のSPAのトップページ(図4)が表示されます。

図3:プロキシページ
図3:プロキシページ
図4:Reactアプリトップページ
図4:Reactアプリトップページ

 図4のページ上部には「ReactApp」と表示されていますが、右端に3つのリンクがあります。これは、第7回でも見てきたようなホーム(Home)、カウンタ(Counter)、気象情報(Fetch data)のページへのリンクに相当します。それぞれクリックすると、「Hello, world!」と表示されているペインがそれぞれ以下のように変化します(図5~図6「Home」は同じページになるだけなので省略)。このとき、ページの移動に伴いURLも変化していることに注意してください。

図5:Counterページ
図5:Counterページ
図6:Fetch dataページ
図6:Fetch dataページ

 ページの内容の変化はReactによるものですが、Fetch dataページではWeb APIとして構成されたバックエンドからデータを取得し、表示しています。

SPAテンプレートの動作モデル

 SPAテンプレートで生成されるアプリケーションは、公開時と開発時で異なる動作モデルとなっています。公開時は、図7のような構成です。

図7:公開時におけるSPAの動作モデル
図7:公開時におけるSPAの動作モデル

 SPAはデプロイ時に、ソースファイルなどがコンパイル・バンドルされて、静的なファイル(index.htmlやJavaScriptファイル)としてサーバのwwwrootフォルダ(静的なファイルを配置して公開するためのフォルダ)に配置されます。SPAが起動するときのリクエストは、静的なファイルのレスポンスとして返り、Webブラウザで実行されます。SPAによるAPIへのリクエストは、Web API単体と同じくサーバで処理されてレスポンスとして返り、SPAの表示などに反映されます。これに対し、開発時は図8のような構成です。

図8:開発時におけるSPAの動作モデル
図8:開発時におけるSPAの動作モデル

 SPAのファイルは、Node.js上で稼働する開発サーバが提供します。SPAが起動するときのリクエストは、SPAプロキシと呼ばれるミドルウェアが開発サーバにリダイレクトし、開発サーバからのレスポンスとして返り、Webブラウザで実行されます。SPAによるAPIへのリクエストは、そのまま透過的にサーバで処理されてレスポンスとして返り、SPAの表示などに反映されます。このように、SPAが公開時のように静的なファイルとして構成されていなくても、Webブラウザからアクセスして起動することができます。

 上記の動作検証において、サーバへのリクエスト(ここではhttp://localhost:5111/)でいったん図3のページが表示されました。これはSPAプロキシが用意したページで、このページが表示されている間に開発サーバを起動し、起動したら開発サーバのURL(ここではhttp://localhost:44494/)にリダイレクトします。リダイレクトの結果、図4のSPAトップページが表示されたわけです。

プロジェクトの構成とファイル

 動作モデルも踏まえて、reactテンプレートによって生成されたプロジェクトを見てみましょう。プロジェクトは、コントローラベースのWeb APIアプリケーションに存在したControllers、Program.csなどのフォルダやファイルに加えて、ClientApp、Pagesといったフォルダから構成されます。Controllers、Program.csはWeb APIのためのフォルダとファイルですが、SPAとともにホスティングするためにその内容は若干異なったものになっています。これらをはじめとして、Web API単独とは異なるファイルについて、まずは見ていきます。

ReactApp.csprojファイルを確認する

 Web API単独の場合の.csprojファイルは、ターゲットの.NETバージョンを明示するなどのシンプルなものでした。reactテンプレートでは、SPAやSPAプロキシに関する設定(1)や、SPAプロキシに対する参照の設定(2)、SPAの公開に関する設定(3)、この他、ビルド時や公開時の設定(4)(5)が細かく記述されています。

リスト ReactApp.csproj
<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>					(1)
    <TargetFramework>net7.0</TargetFramework>
    <Nullable>enable</Nullable>
    <TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
    <TypeScriptToolsVersion>Latest</TypeScriptToolsVersion>
    <IsPackable>false</IsPackable>
    <SpaRoot>ClientApp\</SpaRoot>
    <DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes>
    <SpaProxyServerUrl>http://localhost:44494</SpaProxyServerUrl>	(1')
    <SpaProxyLaunchCommand>npm start</SpaProxyLaunchCommand>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>						(2)
    <PackageReference Include="Microsoft.AspNetCore.SpaProxy" Version="7.0.0" />
  </ItemGroup>

  <ItemGroup>						(3)
    <Content Remove="$(SpaRoot)**" />
    …略…
  </ItemGroup>

  <Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') ">	(4)
    …略…
  </Target>

  <Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish">	(5)
    …略…
  </Target>
</Project>

 上記で、最初にアクセスしたページからReactページにリダイレクトされましたが、これは(1')によるものです。Microsoft.AspNetCore.SpaProxyでは、SPAプロキシを起動するロジックを自動的にアプリケーションに挿入するので、アプリケーションがSPAプロキシ起動のためのコードを記述する必要がありません。

コントローラを確認する

 コントローラファイルについても見てみます。

リスト Controllers/WeatherForecastController.cs
…略…
[HttpGet]					(1)
public IEnumerable<WeatherForecast> Get()
…略…

 異なるのは(1)の箇所のみです。HTTP GETメソッドによるアクション省略時に呼び出されるメソッドですが、name属性が省略されています。これは、後述しますがSwaggerサポートが省略されていることで、OASのoperationId要素へのひも付けが不要だからです。既定のコントローラの動作としては全く同じです。

Program.csファイルを確認する

 Swaggerサポートが省略されるなど、Web API単体のときとProgram.csファイルの内容が大きく異なります。

リスト Program.cs
…略…
var builder = WebApplication.CreateBuilder(args);	(1)
builder.Services.AddControllersWithViews();

var app = builder.Build();				(2)
if (!app.Environment.IsDevelopment())
{
}

app.UseStaticFiles();		(3)
app.UseRouting();		(4)
app.MapControllerRoute(
    name: "default",
    pattern: "{controller}/{action=Index}/{id?}");
app.MapFallbackToFile("index.html");;
app.Run();

 (1)ではビルダーオブジェクトを生成していますが、Swaggerジェネレータの追加はありません。コントローラ利用の追加も、AddControllersメソッドではなくAddControllersWithViewsメソッドとなっています。AddControllersWithViewsメソッドは、単なるコントローラではなくビューを使ったコントローラを追加します。これは、一部ビューを用いる構成になっているためです。

 (2)ではアプリケーションオブジェクトを生成していますが、DebugターゲットにおいてもSwagger UIなどのミドルウェア有効化のコードはありません。

 (3)のUseStaticFilesメソッドは、wwwrootフォルダ以下にある静的なファイルを使用するためのものです。既述の通り、公開時のSPAのファイルはwwwrootフォルダに置かれるので、そのために必要です。

 (4)のUseRoutingメソッドとMapControllerRouteメソッドは、ルーティングを有効にして単一ルートを設定しています。このルートはデフォルトのルールとして利用されます。

 (5)のMapFallbackToFileメソッドは、ルートに一致するページが存在しない場合に表示するファイルを指定しています。指定されているindex.htmlは、SPAのルートとなるファイルです。

Pagesフォルダを確認する

 Pagesフォルダには、Error.cshtmlなどといったエラーメッセージ表示のためのビューが置かれます。これらのファイルについては、第5回第7回で触れました。reactテンプレートでは、SPAプロキシの起動に失敗したときなどのために、内部的に呼び出されます。ここのビューのために、Program.csファイルではAddControllersメソッドではなくAddControllersWithViewsメソッドが呼び出されました。

ClientAppフォルダを確認する

 ClientAppフォルダには、クライアントアプリケーションのコードが収納されます。フォルダ内の構成は、Create-React-App(Reactアプリケーション作成のためのプロジェクト)のテンプレートに準じたものとなっています。バンドラーによるビルドを想定し、publicフォルダに静的なファイル、srcフォルダにReactコンポーネントなどのJavaScriptファイルが配置されます。本稿はReactアプリの構造を解説することが目的ではありませんが、Web APIを利用するSPAとしての動作がどうなっているかに絞って見てみます。まず、以下のファイルの役割を押さえておきましょう。

  • ClientApp/public/index.html……アプリケーションのルート。id属性が"root"であるdiv要素が置かれる
  • ClientApp/src/index.js……上記のdiv要素にレンダリングする内容。Appコンポーネントが置かれる
  • ClientApp/public/AppRoute.js……SPAにおけるルーティングの設定。URLパスとReactコンポーネントの対応が置かれる
  • ClientApp/src/App.js……Appコンポーネントの内容。ルートに対応するコンポーネントを展開する
  • ClientApp/src/components/FetchData.js……URLパス「/fetch-data」に対応するFetchDataコンポーネントの内容

 Web APIから見た場合に重要なのは、最後のFetchData.jsです。これは、「/fetch-data」すなわち気象情報のページに相当するコンポーネントです。このファイルの一部を抜粋します。

リスト ClientApp/src/components/FetchData.js
…略…
componentDidMount() {			(1)
  this.populateWeatherData();
}
…略…
async populateWeatherData() {		(2)
  const response = await fetch('weatherforecast');
  const data = await response.json();
  this.setState({ forecasts: data, loading: false });
}

 (1)は、コンポーネントのマウント後に実行される処理で、(2)で定義されているAPI呼び出しの関数populateWeatherDataを呼び出しています。これにより、ページに必要な気象情報のデータが準備されます。API呼び出しは、fetchメソッドによって非同期に行い、戻り値のJSONデータでコンポーネントのstateオブジェクトを更新しています。更新後に実行されるレンダリングにより、JSONデータを用いて気象情報の表が作成されます。

まとめ

 今回は、.NET 6からサポートされた最小限のWeb APIであるMinimal APIと、これを含めたWeb APIを利用するSPAのテンプレートを紹介しました。

 次回は、gRPCを使用したアプリケーションの開発を紹介します。

この記事は参考になりましたか?

連載通知を行うには会員登録(無料)が必要です。
既に会員の方はを行ってください。
.NET最新版でASP.NET Core連載記事一覧

もっと読む

この記事の著者

WINGSプロジェクト 山内 直(WINGSプロジェクト ヤマウチ ナオ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。出版社を経てフリーランスとして独立。ライター、エディター、デベロッパー、講師業に従事。屋号は「たまデジ。」。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/17891 2023/07/10 11:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー