convert-blazor-server-to-webapp について
.NET 6/7 の Blazor Server アプリを、InteractiveServer の動作を保ちながら .NET 8 以降の Blazor Web App に移行する手順をエージェントに示すスキルです。コンポーネント構成、サービスやエンドポイントの変更、移行後の確認を扱います。
主な機能
- 01ターゲットフレームワークと関連パッケージの更新
- 02ルーティング内容を Routes.razor に移動
- 03_Host.cshtml を App.razor に変換
- 04Blazor サービスとエンドポイントの置き換え
- 05認証状態の移行とアプリの検証
活用シーン
- 01旧 Blazor Server アプリの .NET 8 以降への更新
- 02インタラクティブなサーバー描画を保った新モデルへの移行
- 03移行後のアンチフォージェリやプリレンダリング問題の調査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ dotnet/ skills --skill convert-blazor-server-to-webappインストール後、Claude Code で/convert-blazor-server-to-webappを入力して読み込まれたか確認します。
詳細
Blazor Server アプリを Blazor Web App に変換する
このスキルは、.NET 8 より前の Blazor Server アプリを .NET 8 以降の Blazor Web App に変換する際に、エージェントを支援します。従来のホスティングモデルでは、_Host.cshtml Razor Page をエントリーポイントとして、AddServerSideBlazor/MapBlazorHub を使用します。新しい Blazor Web App モデルでは、App.razor ルートコンポーネントとともに AddRazorComponents/MapRazorComponents を使用し、コンポーネント単位のレンダーモード、強化されたナビゲーション、ストリーミングレンダリングなど、.NET 8 以降の機能を利用できます。変換後のアプリでは、既存のインタラクティブな動作を維持するために InteractiveServer レンダーモードを使用します。
使用するタイミング
- Blazor Server アプリを .NET 6 または .NET 7 から .NET 8 以降に移行する場合
- アプリの
Program.cs(またはStartup.cs)で現在AddServerSideBlazor()とMapBlazorHub()を使用している場合 - アプリが Component Tag Helpers を使うホストページとして
Pages/_Host.cshtml(または_Host.razor)を使用している場合 - インタラクティブなサーバー側レンダリングを維持しつつ、新しい Blazor Web App の機能を採用したい場合
使用しないタイミング
- アプリがすでに
AddRazorComponentsとMapRazorComponentsを使用している場合。 すでに Blazor Web App であり、変換は必要ありません。ここで終了し、アプリがすでに Blazor Web App モデルを使用していることをユーザーに伝えてください。 - Blazor WebAssembly アプリ、またはホスト型 Blazor WebAssembly アプリの場合 — これらには異なる移行手順があります
- アプリを従来の Blazor Server ホスティングモデルのままにする場合(TFM とパッケージだけを更新します)
- アプリのターゲットが .NET Framework の場合 — 先に .NET に移行する必要があります
入力
| 入力 | 必須 | 説明 |
|---|---|---|
| Blazor Server プロジェクト | はい | Blazor Server アプリの .csproj とソースファイル |
| ターゲットフレームワーク | はい | .NET 8 以降(例:net8.0、net9.0、net10.0) |
Program.cs または Startup.cs | はい | アプリのサービスとミドルウェアの構成 |
_Host.cshtml の場所 | 推奨 | 通常は Pages/_Host.cshtml。プロジェクトによっては _Host.razor の場合があります |
ワークフロー
コミット戦略: 移行内容をレビューしやすくし、二分探索できるよう、論理的な各ステップの後にコミットします。
ステップ 1:プロジェクトファイルを更新する
.csproj ファイルを更新します。
- ターゲットフレームワークモニカー(TFM)をターゲットバージョンに変更します。
<TargetFramework>net8.0</TargetFramework> - すべての
Microsoft.AspNetCore.*、Microsoft.EntityFrameworkCore.*、Microsoft.Extensions.*、およびSystem.Net.Http.Jsonパッケージ参照を、対応するバージョンに更新します。
null 許容参照型、暗黙的な using、HTTP/3 サポートなど、Blazor 以外のプロジェクトファイルの変更については、ASP.NET Core の一般的な移行ガイドを参照してください。
ステップ 2:App.razor から Routes.razor を作成する
従来の App.razor には <Router> コンポーネントが含まれています。この内容を新しい Routes.razor ファイルに移動し、App.razor をルート HTML ドキュメントコンポーネントにできるようにします。
- プロジェクトのルートに新しいファイル
Routes.razorを作成します。 App.razorの内容全体をRoutes.razorに移動します。- 内容が
<CascadingAuthenticationState>で囲まれている場合は、そのラッパーを削除します(ステップ 5 でサービスに置き換えます)。 - 次のステップで使うため、
App.razorは空のままにします。
作成される Routes.razor は、次のようになります。
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
</NotFound>
</Router>
アプリが <RouteView> の代わりに <AuthorizeRouteView> を使用している場合は、そのまま残してください。Blazor Web App でも同じように機能します。
ステップ 3:_Host.cshtml を App.razor に変換する
Pages/_Host.cshtml の HTML シェルを、現在空の App.razor に移動し、Razor Page から Razor コンポーネントに変換します。
-
Razor Page ディレクティブを削除する —
@page "/"、@using Microsoft.AspNetCore.Components.Web、@namespace、@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpersを削除します。 -
コンポーネントのインジェクションを追加する — 環境に応じてエラー UI を表示する場合は、次を追加します。
@inject IHostEnvironment Env -
ベースタグを修正する —
<base href="~/" />を<base href="/" />に置き換えます。 -
HeadOutlet コンポーネントタグヘルパーを置き換える — 次を置き換えます。
<component type="typeof(HeadOutlet)" render-mode="ServerPrerendered" />次の内容に置き換えます。
<HeadOutlet @rendermode="InteractiveServer" /> -
App コンポーネントタグヘルパーを Routes に置き換える — 次を置き換えます。
<component type="typeof(App)" render-mode="ServerPrerendered" />次の内容に置き換えます。
<Routes @rendermode="InteractiveServer" /> -
環境タグヘルパーを置き換える — 次を置き換えます。
<environment include="Staging,Production"> An error has occurred. This application may no longer respond until reloaded. </environment> <environment include="Development"> An unhandled exception has occurred. See browser dev tools for details. </environment>次の内容に置き換えます。
@if (Env.IsDevelopment()) { <text> An unhandled exception has occurred. See browser dev tools for details. </text> } else { <text> An error has occurred. This app may no longer respond until reloaded. </text> } -
Blazor スクリプトを更新する — 次を置き換えます。
<script src="_framework/blazor.server.js"></script>次の内容に置き換えます。
<script src="_framework/blazor.web.js"></script> -
レンダーモードのインポートを追加する —
_Imports.razorに次を追加します。@using static Microsoft.AspNetCore.Components.Web.RenderMode -
Pages/_Host.cshtmlを削除する(存在する場合はPages/_Host.cshtml.csも削除します)。
プリレンダリングに関する注意: 元のアプリで render-mode="Server"("ServerPrerendered" ではなく)が使われていた場合、プリレンダリングは無効でした。この状態を維持するには、HeadOutlet と Routes の両方で InteractiveServer の代わりに new InteractiveServerRenderMode(prerender: false) を使用します。
ステップ 4:Program.cs を更新する
Program.cs(古いホスティングパターンを使用しているアプリでは Startup.cs)に次の変更を加えます。
-
Blazor Server サービスを置き換える — 次を置き換えます。
builder.Services.AddServerSideBlazor();次の内容に置き換えます。
builder.Services.AddRazorComponents() .AddInteractiveServerComponents();AddServerSideBlazorにオプション(回線オプション、ハブオプション、詳細なエラーなど)が設定されていた場合は、それらをAddInteractiveServerComponentsに移行します。// Old: builder.Services.AddServerSideBlazor(options => { options.DetailedErrors = true; options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10); }); // New: builder.Services.AddRazorComponents() .AddInteractiveServerComponents(options => { options.DetailedErrors = true; options.DisconnectedCircuitRetentionPeriod = TimeSpan.FromMinutes(10); }); -
Blazor エンドポイントのマッピングを置き換える — 次を置き換えます。
app.MapBlazorHub();次の内容に置き換えます。
app.MapRazorComponents<App>() .AddInteractiveServerRenderMode();AppがApp.razorコンポーネントとして解決されるよう、プロジェクトのルート名前空間を参照するusingステートメントがあることを確認します。 -
フォールバックルートを削除する — 次を削除します。
app.MapFallbackToPage("/_Host"); -
明示的なルーティングミドルウェアを削除する — 存在する場合は次を削除します。
app.UseRouting();エンドポイントルーティングが既定で有効なため、明示的な
UseRouting()は不要になりました。 -
アンチフォージェリーミドルウェアを追加する —
UseAuthentication/UseAuthorizationがある場合は、その後に次を追加します。app.UseAntiforgery();AddRazorComponentsはアンチフォージェリーサービスを自動的に登録しますが、ミドルウェアはパイプラインに明示的に追加する必要があります。追加しない場合、フォームの POST リクエストは 400 エラーで失敗します。
ステップ 5:CascadingAuthenticationState を移行する(使用している場合)
ルーターを <CascadingAuthenticationState> でラップしていた場合:
<CascadingAuthenticationState>コンポーネントのラッパーを削除します(この手順に従っていれば、ステップ 2 ですでに削除されています)。Program.csにカスケード認証状態サービスを追加します。builder.Services.AddCascadingAuthenticationState();
Blazor Web Apps では、コンポーネントラッパーを使う方法はレンダーモードの境界を越えて機能しません。サービスを使う方法では、レンダーモードにかかわらず、すべてのコンポーネントに Task<AuthenticationState> がカスケード値として提供されます。
ステップ 6:推奨される改善(任意)
これらは任意のモダナイゼーション改善であり、移行を機能させるために必須ではありません。これらを提案する場合は、任意であることを明記してください。
UseStaticFilesをMapStaticAssetsに置き換える(.NET 9+):app.MapStaticAssets()は、フィンガープリント、事前圧縮、コンテンツベースの ETag による最適化された静的ファイル配信を提供します。MapStaticAssets のドキュメントを参照してください。- 非同期データ読み込み(
OnInitializedAsync)を行うページに@attribute [StreamRendering]を追加する と、体感パフォーマンスが向上します。ページは最初の同期コンテンツをすぐにレンダリングし、非同期データが届くと再レンダリングします。 <link>タグが_Hostアセンブリ名を参照していた場合は、CSS 分離バンドルの参照を更新し、プロジェクトの実際のアセンブリ名と一致していることを確認します:<link href="{AssemblyName}.styles.css" rel="stylesheet" />。- その他の Blazor 以外の改善(最小ホスティング、HTTP/3、出力キャッシュなど)については、ASP.NET Core の一般的な移行ガイドを参照してください。
ステップ 7:移行を検証する
- 新しいフレームワークを対象にプロジェクトをビルドします。コンパイルエラーがないことを確認してください。
- 削除された API の参照が残っていないか検索します:
AddServerSideBlazorMapBlazorHubMapFallbackToPageblazor.server.js_Host.cshtml
- アプリを実行して、次の点を確認します:
- ページが正しく読み込まれ、レンダリングされる
- インタラクティブ機能(フォーム、イベントハンドラー、SignalR 回線)が動作する
- ページ間のナビゲーションが動作する
- 認証および認可のフローがある場合、それらが動作する
- 既存のテストを実行します。
検証
-
AddServerSideBlazorへの参照が残っていない -
MapBlazorHubへの参照が残っていない -
MapFallbackToPage("/_Host")への参照が残っていない -
blazor.server.jsへの参照が残っていない -
Pages/_Host.cshtmlが削除されている -
App.razorが、完全な HTML ドキュメント構造を持つルートコンポーネントとして機能している -
Routes.razorに<Router>の設定が含まれている -
Program.csでAddRazorComponents().AddInteractiveServerComponents()が使用されている -
Program.csでMapRazorComponents<App>().AddInteractiveServerRenderMode()が使用されている - ミドルウェアパイプラインに
app.UseAntiforgery()が含まれている - アプリで
<CascadingAuthenticationState>を使用していた場合、AddCascadingAuthenticationState()サービス登録に置き換えられている - 対象のフレームワークでアプリが正常にビルドおよび実行される
よくある落とし穴
| 落とし穴 | 解決策 |
|---|---|
UseAntiforgery() ミドルウェアがない | AddRazorComponents は偽造防止サービスを登録しますが、ミドルウェアは明示的に追加する必要があります。app.UseAntiforgery() は UseAuthentication/UseAuthorization の後に配置してください。これがないと、フォームの POST リクエストが 400 エラーで失敗します。 |
blazor.server.js を blazor.web.js に置き換えていない | 古いスクリプトは Blazor Web App モデルでは動作しません。_framework/blazor.server.js への参照をすべて _framework/blazor.web.js に置き換えてください。 |
<CascadingAuthenticationState> ラッパーを削除していない | Blazor Web Apps では、コンポーネントのラッパーはレンダーモードの境界を越えて機能しません。代わりに builder.Services.AddCascadingAuthenticationState() を使用してください。 |
パイプラインに app.UseRouting() を残している | 明示的な UseRouting() は不要になっており、エンドポイントルーティングに干渉する場合があります。他のミドルウェアで特に必要とされない限り、削除してください。 |
プリレンダリングが無効だったのに InteractiveServer を使用している | 元のアプリで render-mode="Server"("ServerPrerendered" ではない)を使用していた場合は、同じ動作を維持するために new InteractiveServerRenderMode(prerender: false) を使用してください。InteractiveServer を使用するとプリレンダリングが有効になり、初期化時に JS 相互運用に依存するコンポーネントで予期しない問題が発生することがあります。 |
AddServerSideBlazor のサーキットオプションを移行していない | サーキットオプション、ハブオプション、または詳細なエラー設定を構成していた場合は、AddInteractiveServerComponents(options => { ... }) に移行してください。そうしないと、それらの設定は通知なく失われます。 |
UseAntiforgery() を認証ミドルウェアより前に配置している | 偽造防止ミドルウェアは UseAuthentication と UseAuthorization の後に配置する必要があります。これより前に配置すると、ユーザーの ID が確立される前に偽造防止の検証が実行されます。 |
| CSS 分離バンドルのリンクでアセンブリ名が間違っている | <link href="{Name}.styles.css"> タグが古いプロジェクト名を参照していた場合は、現在のアセンブリ名に合わせて更新してください。 |
詳細情報
- Blazor Server アプリを Blazor Web App に変換する — 公式のステップごとの移行ガイド
- ASP.NET Core Blazor のレンダーモード — InteractiveServer、InteractiveWebAssembly、InteractiveAuto について
- CascadingAuthenticationState をサービスに移行する — コンポーネントのラッパーをサービスに置き換える方法
- MapStaticAssets — .NET 9 以降で最適化された静的ファイルの配信
- ASP.NET Core 7.0 から 8.0 への移行 — ASP.NET Core のすべての変更を対象とする一般的な移行ガイド
- Blazor でのストリームレンダリング — 非同期データ読み込みに使用する
@attribute [StreamRendering] - カスケード値とレンダーモードの境界 — カスケードパラメーターがレンダーモードの境界を越えない理由