.NET 8 IEndpointRouteBuilder详解

Map

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

c# 复制代码
app.MapGet("/", () => "Hello World!"); // 3 添加路由处理

​ 老规矩,看签名:

c# 复制代码
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 的底层方法:

​ 他的实现就是为了给IEndpointRouteBuilderDataSources 添加一个 RouteEndpointDataSource

c# 复制代码
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

c# 复制代码
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 属性,用于处理多匹配源下的优先级问题。

c# 复制代码
public sealed class RouteEndpoint : Endpoint
{
	public int Order { get; }
	public RoutePattern RoutePattern { get; }
}

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

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

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

c# 复制代码
public delegate Task RequestDelegate(HttpContext context);

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

c# 复制代码
public IList<Func<EndpointFilterFactoryContext, EndpointFilterDelegate, EndpointFilterDelegate>> FilterFactories

EndpointDataSource

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

c# 复制代码
public abstract IReadOnlyList<Endpoint> Endpoints { get; }
public virtual IReadOnlyList<Endpoint> GetGroupedEndpoints(RouteGroupContext context)

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

c# 复制代码
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 是一个路由前缀,ConventionsFinallyConventions 是两个约定hook。它专为 RouteEndpoint 独有,通过 GetGroupedEndpoints 方法,组的前缀和约定,会作用到每一个路由终结点。

c# 复制代码
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>>();
}
相关推荐
EdisonZhou5 天前
使用MCP C# SDK开发MCP Server + Client
llm·aigc·asp.net core·.net core
超软毛毛虫17 天前
ASP.NET Core 模型验证消息的本地化新姿势
asp.net core·localization
lixww.cn2 个月前
ASP.NET Core用MediatR实现领域事件
ddd·asp.net core·mediatr
lixww.cn2 个月前
ASP.NET Core SignalR向部分客户端发消息
javascript·websocket·vue·asp.net core·signalr
lixww.cn2 个月前
ASP.NET Core SignalR的协议协商
asp.net core·signalr
lixww.cn2 个月前
ASP.NET Core SignalR的分布式部署
redis·消息队列·asp.net core·signalr
lixww.cn2 个月前
ASP.NET Core对JWT的封装
asp.net core·jwt·authorize
lixww.cn2 个月前
ASP.NET Core JWT Version
asp.net core·jwt·filter·identity
lixww.cn2 个月前
ASP.NET Core JWT
asp.net core·jwt
lixww.cn2 个月前
ASP.NET Core标识框架Identity
asp.net core·rbac·identity