Middleware (8) — Routing

Feng Gao
8 min readOct 7, 2021

1 Introduction

ASP.NET Core 功能本质上就是将请求的路径找到对应处理过程,在 ASP.NET Core 中将最终处理的过程叫做 EndPoint 。请求的起点到 EndPoint 的过程是叫做 Middleware

这些 Endpoint 就是一个个独立的处理单元,而根据请求的路径找到对应的 Endpoint 过程叫做 Routing 。在 ASP.NET Core 支持以下 Routing 方式。

  • Controller: 通过继承 Controller 来定义 Endpoint ,通常用在 MVCWebAPI 模式中使用
  • Razor Pages
  • SingalR
  • gRPC Service
  • Delegate Lambda 表达式

一个简单的例子如下:

在这个例子中,在访问 /hello/gaufung 路径的时候,返回 Hello gaufung! ; 但是在访问 /hello/123 的时候,返回的结果是 404 Not Found ,因为没有对应的 Endpoint 匹配请求 /hello/123

在这个例子中,使用了 UseRoutingUseEndpoints 两个中间件

  • UseRouting 注册了一个中间件,该中间件的作用是根据请求的 Path 挑选出一个最匹配的 Endpoint ,并且将它存放在 HttpContext 中;
  • UseEndpoints 将之前挑选出来的 Endpoint 进行执行操作,在上述的例子中 endpoints => {} 是用来注册这些 EndPoint

2 RoutePatternPart

对于上述的请求路径的模式 /hello/{name:alpha}ASP.NET Core 会将其拆分成若干个 RoutePatternPart 对象

RoutePatternPart 是一个抽象类,从该类的参数我们可以知道,该类有三种子类,分别为

  1. RoutePatternLiteralPart : 字面的 RoutePatternPart ,例如 hello 就是这个类型
  2. RoutePatternParameterPart : 带参数的 RoutePatternPart , 例如 {name:alpha} ,这个就是指定这个路径必须要是字符组成
  3. RoutePatternSeparatorPart : 这是一个比较特殊的 RoutePatternPart , 比如 /hello/{fileName}.{ext?} 在这个路径中 . 点就是该类型

通过这三个子类,我们就可以构建我们的请求的路径的 Pattern 。在这三种 RoutePatternPart 中, RoutePatternParameterPart 是最重要的的

在这里解释一下其中的部分属性

  • ParameterPolicies 是这个 ParameterPart 的限制条件,比如说 alpha 表明只限制字母表中的字符,而且是一个集合形式因为同个一个 Parameter 可以包含多个限制条件
  • EncodeSlashes 这个意思是是否要将这个 parameterPart 部分的 / 进行编码,比如 {*variable} 则将参数 foo/bar 变成 foo%2fbar ; 而 {**variable} 则仍然为 foo/bar
  • Default 有些时候,路由参数可以给定默认值,比如 /hello/{name:alpha}=gaufung ,那么默认值 gaufung 会被记录下来
  • IsCachedAll 是指路由中是否包含了通匹配符, /hello/{*name} 或者 /hello/{**name} 是匹配了 /hello/ 剩下的所有字符。
  • IsOptional 是指这个路由参数是否为可选的,比如 /hello/{name?} 说明这个 name 这个路由是可选的。
  • Name 是这个参数的名字,比如 /hello/{name:alpha} 中的 name

3. RoutePattern

有了 RoutePatternPart 及其子类,就可以构建 RoutePattern 对象,用来表示完整的 /home/{name:alpha} 对象

  • Defaults 用来保存所有的 ParameterPart 的所有默认值
  • ParameterPolicies 保存啊 ParameterPart 对应的 ParameterPolicy
  • InboundPrecedence URL 匹配的优先级
  • OutboundPrecedence URL 生成的优先级
  • PathSegments 是按照 / 对路径的拆分,每个 RoutePatternPathSegment 可能会包含多个 RoutePatternParametePart ,比如这样的路径 /home/{name:alpha}{size:int}

那么如何去构建这个 RoutePattern 对象呢?答案是 RoutePatternFactory ,它会间接调用 RoutePatternParser.Parse 方法来根据字符串生成。

4 Endpoint

Endpoint 代表了最终处理的单元

  • Metadata 是用来保存这个 Endpoint 的元数据,这是一个 Object 的集合,所以任何类型的数据都可以存放在这个集合中。
  • RequestDelegate 是用来处理 HttpContext 的委托

RouteEndpoint 是用来表示 Route 的终点。

这里 RouteEndpoint 继承了 Endpoint , 并且包含了我们之前讨论的 RoutePattern 类型。

5 RouteEndpointBuilder

RouteEndpointBuilder 是构建 RouteEndpointbuilder 类,该类接受 RouteEndpoint 所需要的参数。

6 IEndpointConventionBuilder

该接口定了以 EndpointBuilder 参数的委托,用来修改 EndpointBuilder 相关的属性

该类实现了 IEndpointConventionBuilder 接口,该类接受一个 EndpointBuilder 对象,然后在 Build 方法中,使用 EndpointBuilder 来构建一个 Endpoint 实例。

7 EndPointDataSource

EndpointDataSource 用来返回所有的 Endpoint 集合,它是一个抽象类, ModelEndpointDataSource 继承了这个抽象类

通过添加 EndpointBuilder 对象,然后 EndPoints 属性会返回这些 endpointBuilderBuild 方法返回 Endpoint

8 DefaultEndpointRouteBuilder

DefaultEndpointRouteBuilder 是用来衔接 IApplicationBuilderEndpointDataSources 就会返回所需要的 EndPoint

9 EndpointRoutingMiddleware

在使用 app.UseRouting 的时候,就会注册一个 EndpointRoutingMiddleware 中间件

在构造函数中,接受 IEndpointRouteBuilder 对象,初始化的时候将 DataSources 构造 CompositeEndpointDataSource 对象,这样在后续的过程中就能就能使用。

  1. 首先判断是否有一个 Endpoint 在当前的 HttpContext
  2. 然后更具 _endpointSource 创建一个 Matcher 对象
  3. 然后调用 MatchAsync 方法,根据当前的 HttpContext 筛选出匹配的 Endpoint ,然后放入到当前的 HttpContext

UseRouting 方法中,创建要给 DefaultEndpointRouteBuilder 对象,并且将它存放在 AppilicationBuilder Properties 中。

10 EndpointMiddleware

该中间件的功能就比较直接

  1. HttpContext 中获取一个 Endpoint
  2. 如果 Endpoint 存在,然后检查一些 metadata
  3. 如果通过检查,就将 EndpointRequestDelegate 方法执行 HttpContext 对象

注意,如果有 Endpoint 命中,执行的过程后直接返回了,并不会调用 next 的方法,否则仍然会执行 next ,这也是终结点的意义。

在使用 UseEndpoint 拓展方法中,注册了 EndpointMiddleware 中间件。

  1. 首先从 IApplicationBuilder 中获取之前注册的 DefaultEndpointRouteBuilder 对象
  2. 然后对参数的 Action<IEndpointRouteBuilder 的委托进行操作,比如说添加自定义个的 Endpoint
  3. 然后将这些 Endpoint 添加到 RouteOption 中,因为会在 URL 生成中也会使用

最后 IEndpointRouteBuilder 也包含了一系列的拓展方法,用来注册 Endpoint

--

--

Feng Gao

A software developer in Microsoft at Suzhou. Most articles spoken language is Chinese. I will try with English when I’m ready