1 Introduction
ASP.NET Core
功能本质上就是将请求的路径找到对应处理过程,在 ASP.NET Core
中将最终处理的过程叫做 EndPoint
。请求的起点到 EndPoint
的过程是叫做 Middleware
。
这些 Endpoint
就是一个个独立的处理单元,而根据请求的路径找到对应的 Endpoint
过程叫做 Routing
。在 ASP.NET Core
支持以下 Routing
方式。
- Controller: 通过继承
Controller
来定义Endpoint
,通常用在MVC
和WebAPI
模式中使用 - Razor Pages
- SingalR
- gRPC Service
- Delegate Lambda 表达式
一个简单的例子如下:
在这个例子中,在访问 /hello/gaufung
路径的时候,返回 Hello gaufung!
; 但是在访问 /hello/123
的时候,返回的结果是 404 Not Found
,因为没有对应的 Endpoint
匹配请求 /hello/123
。
在这个例子中,使用了 UseRouting
和 UseEndpoints
两个中间件
UseRouting
注册了一个中间件,该中间件的作用是根据请求的Path
挑选出一个最匹配的Endpoint
,并且将它存放在HttpContext
中;UseEndpoints
将之前挑选出来的Endpoint
进行执行操作,在上述的例子中endpoints => {}
是用来注册这些EndPoint
。
2 RoutePatternPart
对于上述的请求路径的模式 /hello/{name:alpha}
, ASP.NET Core
会将其拆分成若干个 RoutePatternPart
对象
RoutePatternPart
是一个抽象类,从该类的参数我们可以知道,该类有三种子类,分别为
RoutePatternLiteralPart
: 字面的RoutePatternPart
,例如hello
就是这个类型RoutePatternParameterPart
: 带参数的RoutePatternPart
, 例如{name:alpha}
,这个就是指定这个路径必须要是字符组成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
是构建 RouteEndpoint
的 builder
类,该类接受 RouteEndpoint
所需要的参数。
6 IEndpointConventionBuilder
该接口定了以 EndpointBuilder
参数的委托,用来修改 EndpointBuilder
相关的属性
该类实现了 IEndpointConventionBuilder
接口,该类接受一个 EndpointBuilder
对象,然后在 Build
方法中,使用 EndpointBuilder
来构建一个 Endpoint
实例。
7 EndPointDataSource
EndpointDataSource
用来返回所有的 Endpoint
集合,它是一个抽象类, ModelEndpointDataSource
继承了这个抽象类
通过添加 EndpointBuilder
对象,然后 EndPoints
属性会返回这些 endpointBuilder
的 Build
方法返回 Endpoint
。
8 DefaultEndpointRouteBuilder
DefaultEndpointRouteBuilder
是用来衔接 IApplicationBuilder
和 Endpoint
。 DataSources
就会返回所需要的 EndPoint
。
9 EndpointRoutingMiddleware
在使用 app.UseRouting
的时候,就会注册一个 EndpointRoutingMiddleware
中间件
在构造函数中,接受 IEndpointRouteBuilder
对象,初始化的时候将 DataSources
构造 CompositeEndpointDataSource
对象,这样在后续的过程中就能就能使用。
- 首先判断是否有一个
Endpoint
在当前的HttpContext
中 - 然后更具
_endpointSource
创建一个Matcher
对象 - 然后调用
MatchAsync
方法,根据当前的HttpContext
筛选出匹配的Endpoint
,然后放入到当前的HttpContext
中
UseRouting
方法中,创建要给 DefaultEndpointRouteBuilder
对象,并且将它存放在 AppilicationBuilder
Properties
中。
10 EndpointMiddleware
该中间件的功能就比较直接
- 从
HttpContext
中获取一个Endpoint
- 如果
Endpoint
存在,然后检查一些metadata
- 如果通过检查,就将
Endpoint
的RequestDelegate
方法执行HttpContext
对象
注意,如果有 Endpoint
命中,执行的过程后直接返回了,并不会调用 next
的方法,否则仍然会执行 next
,这也是终结点的意义。
在使用 UseEndpoint
拓展方法中,注册了 EndpointMiddleware
中间件。
- 首先从
IApplicationBuilder
中获取之前注册的DefaultEndpointRouteBuilder
对象 - 然后对参数的
Action<IEndpointRouteBuilder
的委托进行操作,比如说添加自定义个的Endpoint
- 然后将这些
Endpoint
添加到RouteOption
中,因为会在 URL 生成中也会使用
最后 IEndpointRouteBuilder
也包含了一系列的拓展方法,用来注册 Endpoint