.NET 8 IEndpointRouteBuilder详解

本文详细解析了ASP.NETCore中的MapGet方法如何将HTTP处理程序添加到路由构建器,以及RouteEndpoint和EndpointDataSource的概念,展示了它们在路由匹配和HTTP请求处理中的作用。
摘要由CSDN通过智能技术生成
Map

​ 经过对 WebApplication 的初步剖析,我们已经大致对Web应用的骨架有了一定了解,现在我们来看一下Hello World案例中仅剩的一条代码:

app.MapGet("/", () => "Hello World!"); // 3 添加路由处理

老规矩,看签名:

public static RouteHandlerBuilder MapGet(this IEndpointRouteBuilder endpoints,
      [StringSyntax("Route")] string pattern,Delegate handler){
    return endpoints.MapMethods(pattern, (IEnumerable<string>) EndpointRouteBuilderExtensions.GetVerb, handler);
}

我们已经解释过 IEndpointRouteBuilder 的定义了,即为程序定义路由构建的约定。这次看到的是他的拓展方法 Map ,该方法是诸如 MapGetMapPostMapPutMapDeleteMapPatchMapMethods 的底层方法:

他的实现就是为了给IEndpointRouteBuilder 的 DataSources 添加一个 RouteEndpointDataSource

private static RouteHandlerBuilder Map(this IEndpointRouteBuilder endpoints, RoutePattern pattern, Delegate handler, IEnumerable<string> httpMethods, bool isFallback)
{
	return endpoints.GetOrAddRouteEndpointDataSource().AddRouteHandler(pattern, handler, httpMethods, isFallback, RequestDelegateFactory.InferMetadata, RequestDelegateFactory.Create);
}

​ 

RouteEndpointDataSource 继承自 EndpointDataSource,提供一组RouteEndpoint

public override IReadOnlyList<RouteEndpoint> Endpoints
{
	get
	{
		RouteEndpoint[] array = new RouteEndpoint[_routeEntries.Count];
		for (int i = 0; i < _routeEntries.Count; i++)
		{
			array[i] = (RouteEndpoint)CreateRouteEndpointBuilder(_routeEntries[i]).Build();
		}
		return array;
	}
}

Endpoint

​ RouteEndpoint 继承自 Endpoint。多了一个 RoutePattern 属性,用于路由匹配,支持模式路由。还有一个 Order 属性,用于处理多匹配源下的优先级问题。

public sealed class RouteEndpoint : Endpoint
{
	public int Order { get; }
	public RoutePattern RoutePattern { get; }
}

Endpoint 是一个应用程序中路的一个逻辑终结点。

public string? DisplayName { get; } // 终结点名称
public EndpointMetadataCollection Metadata { get; }  // 元数据
public RequestDelegate? RequestDelegate { get; }  // 请求委托

其中 Metadata 就是一个object集合,包含描述、标签、权限等数据,这一般是框架用到的,后面会再次见到它。重点是 RequestDelegateHttpContext 首次登场,相信有过Web开发经验的同学熟悉的不能再熟悉。该委托其实就是一个Func<HttpContext, Task>,用于处理HTTP请求,由于TAP的普及,所以返回的Task

public delegate Task RequestDelegate(HttpContext context);

​ 

Endpoint 一般由 EndpointBuilder 构建,他能够额外组装Filter

public IList<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>> FilterFactories

EndpointDataSource

​ 经过对 MapGet 的剖析我们最终发现,所有的终结点都被挂载在了 EndpointDataSource

public abstract IReadOnlyList<Endpoint> Endpoints { get; }
public virtual IReadOnlyList<Endpoint> GetGroupedEndpoints(RouteGroupContext context)

除了被大家熟悉的 Endpoints 还提供了一个方法 GetGroupedEndpoints:在给定指定前缀和约定的情况下,获取此EndpointDataSource 的所有 Endpoint的。

public virtual IReadOnlyList<Endpoint> GetGroupedEndpoints(RouteGroupContext context)
{
	IReadOnlyList<Endpoint> endpoints = Endpoints;
	RouteEndpoint[] array = new RouteEndpoint[endpoints.Count];
	for (int i = 0; i < endpoints.Count; i++)
	{
		Endpoint endpoint = endpoints[i];
		if (!(endpoint is RouteEndpoint routeEndpoint))
		{
			throw new NotSupportedException(Resources.FormatMapGroup_CustomEndpointUnsupported(endpoint.GetType()));
		}
		RoutePattern routePattern = RoutePatternFactory.Combine(context.Prefix, routeEndpoint.RoutePattern);
		RouteEndpointBuilder routeEndpointBuilder = new RouteEndpointBuilder(routeEndpoint.RequestDelegate, routePattern, routeEndpoint.Order)
		{
			DisplayName = routeEndpoint.DisplayName,
			ApplicationServices = context.ApplicationServices
		};
		foreach (Action<EndpointBuilder> convention in context.Conventions)
		{
			convention(routeEndpointBuilder);
		}
		foreach (object metadatum in routeEndpoint.Metadata)
		{
			routeEndpointBuilder.Metadata.Add(metadatum);
		}
		foreach (Action<EndpointBuilder> finallyConvention in context.FinallyConventions)
		{
			finallyConvention(routeEndpointBuilder);
		}
		array[i] = (RouteEndpoint)routeEndpointBuilder.Build();
	}
	return array;
}

通过剖析 RouteGroupContext,很容易发觉,Prefix 是一个路由前缀,Conventions 和 FinallyConventions 是两个约定hook。它专为 RouteEndpoint 独有,通过 GetGroupedEndpoints 方法,组的前缀和约定,会作用到每一个路由终结点。

public sealed class RouteGroupContext
{
    public required RoutePattern Prefix { get; init; }
    public IReadOnlyList<Action<EndpointBuilder>> Conventions { get; init; } = Array.Empty<Action<EndpointBuilder>>();
    public IReadOnlyList<Action<EndpointBuilder>> FinallyConventions { get; init; } = Array.Empty<Action<EndpointBuilder>>();
}

文章转载自:xiaolipro

原文链接:https://www.cnblogs.com/xiaolipro/p/17818017.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值