在ASP.NET Core最小API中,如何获取路由名称?

问题描述 投票:0回答:2

我想测试最小API

WebApplication
的路由。给定
HttpRequestMessage
,如何获取路线名称?

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRouting().Configure<RouteOptions>(options => {});
var app = builder.Build();

app.MapGet("/hello", () => "Hello!").WithName(RouteNames.Hello);
app.MapGet("/world", () => "World!").WithName(RouteNames.World);

//app.Run();

var request = new HttpRequestMessage(HttpMethod.Get, "/world");
var routeName = GetRouteName(app, request);
Console.WriteLine($"routeName: {routeName}");

string? GetRouteName(WebApplication app, HttpRequestMessage request)
{
    return null; // TODO: Implement
}

static class RouteNames
{
    public const string Hello = "Hello";
    public const string World = "World";
}

https://github.com/dotnet/aspnetcore/issues/48034 提出功能请求。


我接受了克里斯的回答。它让我走上了正轨。我希望能够在不执行实际端点的情况下获取端点名称,但我会将其留到另一天。也许我只是不打电话

next
。使用他的答案,我们可以将端点填充到响应中。可以创建一个测试工具来测试我们所有的命名端点。这是使用
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="7.0.5" />
的工作代码:


using Microsoft.AspNetCore.TestHost;
using System.Diagnostics;

var host = new HostBuilder()
    .ConfigureWebHost(webHost => webHost.UseTestServer().Configure(app =>
    {
        app.UseRouting();
        app.Use((context, next) =>
        {
            if (context.GetEndpoint() is Endpoint endpoint)
            {
                var endpointName = endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()?.EndpointName;
                if (endpointName != null)
                {
                    context.Response.Headers.Add(TagKeys.EndpointName, endpointName);
                }
            }
            return next();
        });
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapGet("/hello", () => "Hello!").WithName(RouteNames.Hello);
            endpoints.MapGet("/world", () => "World!").WithName(RouteNames.World);
        });
    })
    .ConfigureServices(services =>
    {
        services.AddRouting().Configure<RouteOptions>(routeOptions => { });
    }
    )
    ).Build();
host.Start();
var httpClient = host.GetTestClient();

await PrintEndpointName(httpClient, new HttpRequestMessage(HttpMethod.Get, "/"));
await PrintEndpointName(httpClient, new HttpRequestMessage(HttpMethod.Get, "/hello"));
await PrintEndpointName(httpClient, new HttpRequestMessage(HttpMethod.Get, "/world"));

async Task PrintEndpointName(HttpClient httpClient, HttpRequestMessage request)
{
    var httpResponse = await httpClient.SendAsync(request);
    IEnumerable<string>? headers;
    httpResponse.Headers.TryGetValues(TagKeys.EndpointName, out headers);
    var endpointName = headers?.FirstOrDefault();
    Debug.WriteLine($"{((int)httpResponse.StatusCode)} {endpointName}");
}

static class RouteNames
{
    public const string Hello = "Hello";
    public const string World = "World";
}

static class TagKeys
{
    public const string EndpointName = "endpoint.name";
}
c# asp.net-core minimal-apis asp.net-minimal-apis
2个回答
2
投票

尚不清楚您打算在服务器端还是客户端使用它。它似乎是服务器端。如果您在服务器端,那么您可以在定义时获取映射,而无需任何额外的查找。

匹配时,这是世界上最简单的中间件,它将捕获被调用的端点/路由的名称(如果有):

app.Use( ( context, next ) =>
{
    if ( context.GetEndpoint() is Endpoint endpoint )
    {
        var name = endpoint.Metadata.GetMetadata<IEndpointNameMetadata>()?.EndpointName ?? "(no name)";

        Debug.WriteLine( $"Endpoint = {name}" );
    }

    return next();
} );

在最小 API 的上下文中,

WithName
将生成
IEndpointNameMetadata.EndpointName
IRouteNameMetadata.RouteName
。您可以使用其中一个或添加更多防御来覆盖两者。这种方法也应该适用于基于控制器的 API。

它是指从 URL 中反向查找路由/端点名称,这不受支持,而且说实话,无法真正按预期工作。例如,如果传入请求 URL 为

values/42
,则它永远不会与定义的路由模板
values/{id}
匹配。恒定的路线模板可以工作,但很少见。

我不想阻止你说这是“不可能”,但这真的非常非常困难,可能很脆弱,而且可能不值得付出努力。恕我直言,最好的选项是让路由系统完成其工作,然后从匹配的端点(如果有)中提取您感兴趣的元数据。 这是在看到上面的讨论后,我正在寻找类似的东西,顺便说一句,结果更简单,如下: var endpointName = context.GetEndpoint()?.Metadata.OfType().First().EndpointName;


0
投票
© www.soinside.com 2019 - 2024. All rights reserved.