写点什么

.NET 8 IEndpointRouteBuilder 详解

  • 2023-11-09
    福建
  • 本文字数:2378 字

    阅读完需:约 8 分钟

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

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
.NET 8 IEndpointRouteBuilder详解_.net_不在线第一只蜗牛_InfoQ写作社区