本稿では、ASP.NET Coreにおける新しいルーティング手法について解説します。エンドポイントルーティングとは何か、どのように機能するか、どこで使用されるか、そしてどのように独自のルーティングを作成するかを学びます。
エンドポイントルーティングの探求 エンドポイントルーティングを理解するには、まずエンドポイントとルーティングが何であるかを知る必要があります。 エンドポイントは、アプリケーションの一部であり、ルーティングが受信リクエストをそれにマッピングした際に実行されます。 クライアントは通常、サーバーからリソースを要求します。ほとんどの場合、クライアントはブラウザです。リソースは特定のターゲットを指すURLによって定義されます。Webページだけでなく、Web APIから特定のJSONデータを要求するモバイルアプリケーションも該当します。 一方、実行されるエンドポイントは特定のルートにマッピングされます。ASP.NET Core開発者は、以下のようなルーティングパターンに慣れているでしょう。
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllerRoute(name: "default", pattern: "{controller=Home}/{action=Index}/{id?}");
});
もしルートまたはルートパターンが受信リクエストのURLと一致すれば、リクエストはそのエンドポイントにマッピングされます。 ASP.NET Coreは、以下のエンドポイントにマッピングできます。
コントローラー (MVCやWeb APIなど) Razor Pages SignalR (およびBlazor Server) gRPCサービス ヘルスチェック
ほとんどのエンドポイントは非常にシンプルなルートパターンを使用します。MVCとWeb APIエンドポイントのみがより複雑なパターンを使用します。Razor Pagesのルート定義は、実際のページのフォルダーとファイル構造に基づいています。 エンドポイントルーティングが導入される前は、ルーティングは主にMVCとWeb APIで使用されていました。Razor Pagesの暗黙的なルーティングは組み込みされており、SignalRにはルーティングの概念がありませんでした。BlazorとgRPCは当時まだ存在せず、ヘルスチェックは当初ミドルウェアコンポーネントとして実装されていました。 エンドポイントルーティングは、ルーティングを実際のエンドポイントから分離することを目的としています。分かりやすく言うと、URLアドレスと実際に実行されるアクションを分離し、フレームワークをより柔軟にします。これは、新しいエンドポイントが独自のルーティングを実装する必要がないことを意味します。
カスタムエンドポイントの作成 エンドポイントを作成する最も簡単な方法は、ラムダ式を使用することです。
app.Map("/custom-path", async (httpContext) => {
await httpContext.Response.WriteAsync("応答");
});
ここでは、/custom-pathルートを単純なエンドポイントにマッピングし、そのエンドポイントは「応答」という単語をレスポンスストリームに書き込みます。 また、GET、POST、PUT、DELETEなどの特定のHTTPメソッドをエンドポイントにマッピングすることもできます。以下のコードは、GETメソッドとPOSTメソッドをマッピングする方法を示しています。
app.MapGet("/api/get", async (ctx) => {
await ctx.Response.WriteAsync("GETリクエスト");
});
app.MapPost("/api/post", async (ctx) => {
await ctx.Response.WriteAsync("POSTリクエスト");
});
さらに、2つ以上のHTTPメソッドを1つのエンドポイントにマッピングすることも可能です。
app.MapMethods("/api/methods", new[] { "DELETE", "PUT" },
async (ctx) => {
await ctx.Response.WriteAsync("複数メソッド");
});
これらのエンドポイントマッピングは、第8章で見たラムダベースのミドルウェアコンポーネントに似ています。これらのパイプラインミドルウェアは、HTMLベースのビューやJSON構造化データなどの結果を返します。しかし、エンドポイントルーティングはより柔軟な出力方法であり、ASP.NET Core 3.0以降のすべてのバージョンで使用されるべきです。 第8章では、このような分岐パイプラインを見ました。
app.Map("/branch", (branchBuilder) => { // …… });
上記の方法もルートを作成しますが、/branchで始まるURLのみをリッスンします。/branch/{id:int?}のようなパターンを処理するルートエンジンが必要な場合、/branch/456を/branch/abcではなくマッチさせるには、前述の新しいルーティングを使用する必要があります。 そして、ラムダベースのエンドポイントマッピングは、シンプルなシナリオには非常に便利です。しかし、これらはProgram.csで定義されるため、より複雑なシナリオを実装する場合、コードの保守性が悪化する可能性があります。 したがって、カスタムエンドポイントを作成するためのより構造化された方法を見つける必要があります。
より複雑なエンドポイントの作成 次に、ヘルスチェックエンドポイントの例を作成します。これは、Kubernetesクラスタでアプリケーションを実行する場合に必要となる、システムの健全性を検証するためのものです。 開発者の観点からAPIインターフェースを定義しましょう。まず、IEndpointRouteBuilderオブジェクトにMapCustomHealthCheckメソッドを追加します。これはまだ実装されていません。
app.MapCustomHealthCheck("/health");
app.MapControllerRoute(name: "default",pattern:"{controller=Home}/{action=Index}/{id?}");
混乱を避けるため、ここでは以前と同様の方法で新しいインターフェースを追加し、後で実装を進めます。 MapCustomHealthCheckという名前の静的クラスを作成し、MapCustomHealthCheckメソッドを配置します。このオブジェクトはIEndpointRouteBuilderインターフェースを拡張し、IEndpointConventionBuilderオブジェクトを返します。
namespace RoutingSample;
public static class CustomEndpointExtensions {
public static IEndpointConventionBuilder MapCustomHealthCheck(this IEndpointRouteBuilder endpoints, string pattern = "/health")
{
// ...
}
}
上記はスケルトンコードです。実際のインターフェースは、次のミドルウェアのように、次のミドルウェアコンポーネントを呼び出さず、レスポンスストリームの出力を作成する終端ミドルウェアとして実装されます。
namespace RoutingSample;
public class HealthCheckMiddleware {
private readonly ILogger<HealthCheckMiddleware> _logger;
public HealthCheckMiddleware(RequestDelegate next, ILogger<HealthCheckMiddleware> logger)
{
_logger = logger;
}
public async Task Invoke(HttpContext context) {
// ここにいくつかのチェックを追加...
context.Response.StatusCode = 200;
context.Response.ContentType = "text/plain";
await context.Response.WriteAsync("OK");
}
}
実際の作業はInvokeメソッドで行われます。現在は200ステータスコードとOKステータスのレスポンスのみを示していますが、データベースや関連サービスの可用性をチェックするなど、このメソッドを自由に拡張できます。 次に、この終端ミドルウェアを使用します。MapCustomHealthCheckメソッドのスケルトンに戻ります。ここで独自のパイプラインを作成し、指定されたパイプラインにマッピングします。
var customPipeline = endpoints.CreateApplicationBuilder().UseMiddleware<HealthCheckMiddleware>().Build();
return endpoints.Map(pattern, customPipeline).WithDisplayName("My custom health checks");
この方法により、この新しいパイプラインにさらに多くのミドルウェアを追加できます。WithDisplayName拡張メソッドは、インターフェースの表示名を設定します。次にF5キーを押してプログラムを起動し、ブラウザでhttps://localhost:7111/healthにアクセスします。以下が表示されます。 注:ポート番号は異なる場合があります。また、既存の終端ミドルウェアコンポーネントをルートインターフェースに変換して、より柔軟なルーティングを構成することもできます。