ASP.NET Core 中间件的配置方法能够分为以上三种,对应的Helper方法分别是:Run(), Use(), Map()。框架
实际中呢,Use()这个helper方法用的最多。函数
这是一个使用Run方法调用的中间件,Run方法会终止整个中间件管道,它应该返回某种类型的响应。3d
Use看起来和Run差很少,可是多了一个next参数。next能够用来调用请求管道中的下一个中间件。而当前的中间件也能够本身返回响应,这就忽略掉了next调用。中间件
在next调用以前,咱们能够写一些请求进来的逻辑,而在next调用以后,就至关于返回响应了,这时候也能够写一些逻辑。对象
在本例中,咱们下面还使用了Run方法注册了另外一个中间件。由于中间件会按照它们注册的顺序进行调用,因此在第一个Use方法里执行next.Invoke()的时候,就会执行下面Run所调用的中间件。blog
Map方法能够把请求路由到其它的中间件上面。路由
在这里,若是请求的路径以/jump结尾,那么它所对应的handler方法,也就是HereIAm方法就会被调用,并返回一个响应。源码
而若是请求的路径不是以/jump结尾,那么HereIAm方法和里面的中间件就不会被调用。io
上面的例子,我都是使用的inline写法的中间件。模板
而实际上,中间件一般是自成一个类。中间件的类须要相似这样:
自定义的中间件类须要由这几部分组成:
ASP.NET Core 3.x 使用了一套叫作 Endpoint Routing 的路由系统。这套路由系统在ASP.NET Core 2.2的时候就已经露面了。
这套Endpoint Routing路由系统提供了更强大的功能和灵活性,以便能更好的处理请求。
咱们先回顾一下早期版本的ASP.NET Core的路由系统:
在早期的ASP.NET Core框架里,HTTP请求进入中间件管道,在管道的结尾处,有一个Router中间件,也就是路由中间件。这个路由中间件会把HTTP请求和路由数据发送给MVC的一个组件,它叫作MVC Router Handler。
这个MVC 路由 Handler就会使用这些路由数据来决定哪一个Controller的Action方法应该来负责处理这个请求。
而后 Router中间件就会执行被选中的Action方法,并生成响应,而这个响应就会顺着中间件的管道原路返回。
为何早期的这套路由系统被抛弃了?它有什么问题?
第一个问题就是,在被MVC处理以前,其它的中间件不知道最后哪一个Action方法会被选中来处理这个请求。这对像Authorization(受权),Cors这样的中间件会形成很大的困扰,由于他们不能提早知道该请求会被送往哪里。
第二个问题就是,这套流程会把MVC和路由的职责紧密的耦合在一块儿,而实际MVC的本职工做应该仅仅就是生成响应。
Endpoint routing 路由系统,它把MVC的路由功能抽象剥离出来,并放置到中间件管道里,从而解决了早期ASP.NET Core路由系统的那两个问题。
而在Endpoint Routing 路由系统里,其实一共有两个中间件,它们的名称有点容易混淆,可是你只要记住他们的职责便可:
因此,Endpoint Routing的流程图大体以下:
在这里,Endpoint Routing 中间件会分析进来的请求,并把它和在程序中注册的Endpoints进行比较。它会使用这些 Endpoints 上面的元数据来决定哪一个是处理该请求的最佳人选。而后,这个选中的Endpoint 就会被赋给请求的对象,而其它后续的中间件就能够根据这个选中的Endpoint,来作一些本身的决策。在全部的中间件都执行完以后,这个被选中的Endpoint最终将被 Endpoint中间件所执行,而与之关联的Action方法就会被执行。
Endpoint是这样的一些类,这些类包含一个请求的委托(Request Delegate)和其它的一些元数据,使用这些东西,Endpoint类能够生成一个响应。
而在MVC的上下文中,这个请求委托就是一个包装类,它包装了一个方法,这个方法能够实例化一个Controller并执行选中的Action方法。
Endpoint还包含元数据,这些元数据用来决定他们的请求委托是否应该用于当前的请求,仍是另有其它用途。
提及来可能有点迷糊,一会咱们看看源码。
以前咱们见过,ASP.NET Core里面的Startup.cs里面有两个方法,分别是ConfigureServices()和Configure(),它们的职责就是注册应用的一些服务和构建中间件请求管道。
而Startup.cs同时也是应用的路由以及Endpoint做为其它步骤的一分部进行注册的地方。
看图:
在ASP.NET Core应用程序启动的时候,一个叫作ControllerActionEndpointDataSource的类做为应用程序级别的服务被建立了。
这个类里面有一个叫作CreateEndpoints()的方法,它会获取全部Controller的Action方法。
而后针对每一个Action方法,它会建立一个Endpoint实例。这些Endpoint实例就是包装了Controller和Action方法的执行的请求委托(Request Delegate)。
ControllerActionEndpointDataSource里面包存储着在应用程序里注册的路由模板。
而针对每一个Endpoint,它要么与某个按约定的路由模板相关联,要么与某个Controller Action上的Attribute路由信息相关联。而这些路由在稍后就会被用来将Endpoint与进来的请求进行匹配。
App启动那部分就不说了。
第一个HTTP请求进来的时候,Endpoint Routing中间件就会把请求映射到一个Endpoint上。它会使用之App启动时建立好的EndpointDataSource,来遍历查找全部可用的Endpoint,并检查和它关联的路由以及元数据,来找到最匹配的Endpoint。
一旦某个Endpoint实例被选中,它就会被附加在请求的对象上,这样它就能够被后续的中间件所使用了。
最后在管道的尽头,当 Endpoint中间件运行的时候,它就会执行Endpoint所关联的请求委托。这个请求委托就会触发和实例化选中的Controller和Action方法,并产生响应。最后响应再从中间件管道原路返回。