ASP.NET CoreプロジェクトにSvelteを組み込む (SvelteKitは使わない)
はじめに
記事:「ASP.NET CoreプロジェクトにSvelteを組み込む」では、ASP.NET Core APIプロジェクトにSvelteKitを組み込み、フロントエンドとしてSvelteを使えるようにしました。
この記事では、SvelteKitを使わずに、SvelteのみをASP.NET Core APIプロジェクトに組み込む方法について説明します。
ASP.NET Core Web APIプロジェクトの作成
まずは、Visual StudioでASP.NET Core Web APIプロジェクトを作成します。

ここでは名前をSvelteAspNetCoreとしました。
Svelteのプロジェクトを追加
- 
タームナルを開いて、作成したC#のプロジェクトのフォルダーに移動します。ソリューションフォルダーではなく、プロジェクトフォルダーなので間違えないようにしてください。
 - 
以下のコマンドを実行します。
npm create vite@latest - 
質問に答えます。これによりSveltreKitを含まないSvelteだけのプロジェクトが作成されます。
Project name: ... ClientApp Package name: ... clientapp Select a framework: » Svelte Select a variant: » JavaScript - 
Svelteプロジェクトの作成が終わったら、以下のコマンドを実行
cd clientapp npm install - 
パッケージのインストールが終わると、Visual Studioのソリューションエクスプローラーは以下のようになります。

この構造により、ASP.NET CoreプロジェクトとSvelteアプリケーションを同じリポジトリで管理しやすくなります。
 - 
vite.config.jsを以下のように書き換えます。
import { defineConfig } from 'vite' import { svelte } from '@sveltejs/vite-plugin-svelte' export default defineConfig({ plugins: [svelte()], build: { outDir: '../wwwroot', emptyOutDir: true } })これでビルド時の出力フォルダーをwwwrootにしています。
 - 
ClientAppフォルダーで、以下のコマンドでSvelteプロジェクトをビルドします。
npm run build - 
wwwrootフォルダーが作成されたことを確認します。

 - 
続いて、以下のコマンドでSvelteアプリだけを動かしてみます。
npm run dev - 
http://localhost:5173/にアクセスします。以下のページが表示されればOKです。

 
ASP.NET Coreでsvelteをホストする
Program.csの変更
ASP.NET CoreのホストでSvelteが動作するよう、Program.csを以下のように変更します。追加するのは、+記号のある2行です。
        app.UseHttpsRedirection();
        app.UseAuthorization();
+       app.UseStaticFiles();  
        app.MapControllers();
+       app.MapFallbackToFile("index.html");
        app.Run();
MapFallbackToFile は、特定のルートが見つからない場合に指定されたファイルにフォールバックするために使用されます。
これにより、サーバー側(C#側)で特定のルートが見つからない場合でも、index.htmlにルーティングされ、クライアント側のルーティングが行われるようになります。
SvelteAspNetCore.csprojファイルの変更
C#のビルド時に、Svelte側もビルドするよう以下をSvelteAspNetCore.csprojファイルに追加します。
  <Target Name="NpmBuild" BeforeTargets="Build">
    <Message Text="#Start npm run build" Importance="high" />
    <RemoveDir Directories="wwwroot" />
    <Exec Command="npm run build" WorkingDirectory="./ClientApp" />
  </Target>
デバッグで動作確認
F5キーでデバッグ実行してみます。
APIの動作確認をするSwaggerのページが表示されますが、ブラウザのURLを
https://localhost:7096/
に変えてみます。(ポート番号は環境によて異なるかもしれません)
これで、再度Svelteのページが表示されればOKです。

デバッグの初期URLを変更
デバッグするたびに、ブラウザのアドレス欄のURLを変更するのは面倒なので、初期URLを変更しSvelteのページが開くように変更します。
swaggerのページを開きたい場合は、直接以下のURLを開くこととします。
https://localhost:7096/swagger/index.html
プロジェクトのプロパティ画面を開き、デバッグ設定ダイアログを開き、URL欄をクリアします。

API連携をしてみる
C#のWeatherForecastControllerを変更
まずは、APIのルーティングとSvelteのルーティングを区別するために、このプロジェクトではC#で作成するAPIは、
https://localhost:7096/api/....
のように必ず、apiで始まるようにします。
サンプルで作成されたWeatherForecast APIのコードを変更します。[Route]属性の引数を"api/[controller]"に変更します。
using Microsoft.AspNetCore.Mvc;
namespace SvelteAspNetApi.Controllers;
[ApiController]
[Route("api/[controller]")]
public class WeatherForecastController : ControllerBase 
Svelteのルーティングの設定
SvelteKitを使わないため、このままではルーティングができません。そのため、svelte-routingを利用することとします。
以下のコマンドで、インストールします。
npm i -D svelte-routing
Svelteにページを追加
Menu.svelte
ClientApp/src/pagesフォルダーを作成し、その下に Menu.svelteファイルを新規作成します。これが最初に表示されるページとなります。
<script>
</script>
<nav>
  <a href="/weather-forcast">Weather</a>
</nav>
<style>
</style>
WeatherForecast.svelte
続いて、WeatherForecast APIを呼び出し、その結果を表示するページを作成します。
以下のように、ClientApp/src/pagesフォルダーの下に WeatherForecast.svelteファイルを新規作成します。
<script>
  import { onMount } from 'svelte';
  let data = [];
  onMount(async () => {
    const response = await fetch('/api/WeatherForecast');
    data = await response.json();
  });
</script>
<h1>Data from API</h1>
{#each data as item}
  <div>
    <span>{item.date}</span>
    <span>{item.temperatureC}</span>
    <span>{item.temperatureF}</span>
    <span>{item.summary}</span>
  </div>
{/each}
App.svelte
App.svelteを以下のように変更します。<Router>タグは、svelte-routingが提要するタグです。<Router>タグの中身が、それぞれのpathで示したSvelteコンポーネント(svelteファイル)で切り替わることになります。svelte-routingの使い方は、公式サイトなどで確認してください。
<script>
  import { Route, Router } from "svelte-routing";
  import Menu from './pages/Menu.svelte';
  import WeatherForecast from './pages/WeatherForecast.svelte';
</script>
<main>
    <h1>Asp.Net Core + Svelte</h1>
    <Router>
      <Route path="" component={Menu} exact />
      <Route path="/weather-forcast" component={WeatherForecast} />
    </Router>
</main>
<style>
  h1 {
    margin: 0;
    padding: 0;
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    text-align: center;
  }
</style>
SvelteKitの場合は、すべてのページのファイル名が+page.svelteになりますが、svelte-routingでは自由度が高いので、ルーティングとsvelteファイルの関係を自由に定義することができます。ページを追加するたびに<Route>タグを追加しないといけないのが面倒ですが、筆者はプログラムを作成しpagesフォルダーのファイルから自動で<Route>タグを追加するようにしています。
package.jsonを編集し、このプログラムをnpm run build時に実行するようにすれば、自動化することも可能です。
動作確認
それでは、ビルドして、F5で実行してみます。
起動すると、以下のページが表示されます。

ここで、Weatherのリンクをクリックすると以下の表示に変化します。APIが呼ばれその結果を表示していることを確認できます。

Discussion