WebApi 2 中 OData 控制器 http 请求的完整生命周期是啥

Posted

技术标签:

【中文标题】WebApi 2 中 OData 控制器 http 请求的完整生命周期是啥【英文标题】:What is the complete lifecycle of an OData controller http request in WebApi 2WebApi 2 中 OData 控制器 http 请求的完整生命周期是什么 【发布时间】:2017-07-26 11:05:35 【问题描述】:

我想知道在 IIS 中托管的 ODataController 上的 odata http 请求的完整生命周期是什么。

例如:

IIS 流水线步骤有哪些? 进入 ASP.NET 控制器区域时如何处理请求? 何时应用路由? HttpPostApplyFilter 等属性何时应用?

【问题讨论】:

您基本上是在询问他们官方文档中定义的所有内容,可以在这里找到odata.github.io/WebApi 【参考方案1】:

也许这个帖子可以帮助你: The ASP.NET Web API 2 HTTP Message Lifecycle in 43 Easy Steps

一切都从 IIS 开始:

    IIS(或 OWIN 自托管)收到请求。

    然后将请求传递给HttpServer 的实例。

    HttpServer 负责调度 HttpRequestMessage 对象。

    HttpRequestMessage 提供对请求的强类型访问。

    如果管道上存在一个或多个DelegatingHandler 的全局实例,则将请求传递给它。请求按照将所述实例添加到管道中的顺序到达 DelegatingHandler 的实例。

DelegatingHandler 实例可以跳过管道的其余部分并创建自己的响应。我在Custom Validation with FluentValidation 帖子中就是这样做的。

    如果 HttpRequestMessage 传递了 DelegatingHandler 实例(或不存在此类处理程序),则请求继续到 HttpRoutingDispatcher 实例。

HttpRoutingDispatcher 根据匹配的路由选择要调用的路由处理程序。如果不存在这样的路由(例如 Route.Handler 为空,如图所示),则请求直接进行到步骤 10。

    如果给定路由存在路由处理程序,则将 HttpRequestMessage 发送到该处理程序。 可以有DelegatingHandler attached to individual routes 的实例。如果存在此类处理程序,则请求将发送给它们(按照它们添加到管道的顺序)。 HttpMessageHandler 的实例然后处理请求。如果您提供自定义 HttpMessageHandler,则所述处理程序可以选择将请求返回到“主”路径或自定义端点。 HttpControllerDispatcher 实例接收请求,该实例会将请求路由到由请求的 URL 确定的适当路由。 HttpControllerDispatcher 选择适当的控制器将请求路由到。 IHttpControllerSelector 的一个实例为给定的 HttpMessage 选择适当的 HttpControllerDescriptor。 IHttpControllerSelector 调用 IHttpControllerTypeResolver 的一个实例,该实例最终会调用... IAssembliesResolver 的一个实例,它最终选择适当的控制器并将其从第 11 步返回给 HttpControllerDispatcher。 注意:如果您实施依赖注入,IAssembliesResolver 将被您注册的任何容器替换。 一旦 HttpControllerDispatcher 引用了适当的控制器,它就会调用 IHttpControllerActivator 上的 Create() 方法... 创建实际控制器并将其返回给 Dispatcher。然后调度程序将请求发送到 Select Controller Action 例程,如下所示。 我们现在有一个 ApiController 实例,它代表请求被路由到的实际控制器类。所述实例调用 IHttpActionSelector 上的 SelectAction() 方法... 它返回一个 HttpActionDescriptor 实例,表示需要调用的操作。 一旦管道确定了将请求路由到哪个操作,它就会执行插入到管道中的任何身份验证过滤器(全局或本地调用的操作)。 这些过滤器允许您对单个操作、整个控制器或整个应用程序的全局请求进行身份验证。任何存在的过滤器都按照它们添加到管道的顺序执行(首先是全局过滤器,然后是控制器级过滤器,然后是操作级过滤器)。 然后请求继续到 [Authorization Filters] 层,其中存在的任何授权过滤器都将应用于请求。 授权过滤器可以选择创建自己的响应并将其发回,而不是允许请求通过管道进行。这些过滤器的应用方式与身份验证过滤器相同(全局、控制器级别、操作级别)。请注意,授权过滤器只能用于请求,而不是响应,因为假设如果存在响应,则用户有权生成它。 请求现在进入模型绑定过程,该过程显示在主海报的下一部分。操作所需的每个参数都可以通过三个独立路径之一绑定到其值。绑定系统使用哪条路径取决于请求中所需值的位置。 如果实体主体中存在动作参数值所需的数据,则Web API读取请求主体; FormatterParameterBinding 的一个实例将调用适当的格式化程序类... 将值绑定到媒体类型(使用 MediaTypeFormatter)... 这会产生一个新的复杂类型。 如果 URL 或查询字符串中存在参数值所需的数据,则将所述 URL 传递到 IModelBinder 实例,该实例使用 IValueProvider 将值映射到模型(有关更多信息,请参阅 Phil Haack 关于此主题的帖子) .... 这会产生一个简单的类型。 如果存在自定义 HttpParameterBinding,系统将使用该自定义绑定来构建值... 这会导致任何类型(简单或复杂)的对象都可映射(请参阅 Mike Stall 关于此主题的精彩系列)。 现在请求已绑定到模型,它会通过管道中可能存在的任何操作过滤器(全局或仅用于被调用的操作)。 一旦通过了动作过滤器,动作本身就会被调用,系统会等待它的响应。 如果操作产生异常并且存在异常过滤器,则异常过滤器会接收并处理异常。 如果未发生异常,则该操作通过运行结果转换子例程生成一个 HttpResponseMessage 实例,如下一个屏幕截图所示。 如果返回类型已经是一个HttpResponseMessage,我们不需要做任何转换,所以通过返回。 如果返回类型为 void,.NET 将返回状态为 204 No Content 的 HttpResponseMessage。 如果返回类型是IHttpActionResult,调用ExecuteAsync方法创建一个HttpResponseMessage。 在您使用 return Ok(); 的任何 Web API 方法中或返回 BadRequest();或类似的东西,返回语句遵循这个过程,而不是任何其他过程,因为这些操作的返回类型是 IHttpActionResult。 对于所有其他类型,.NET 将创建一个 HttpResponseMessage 并将返回的序列化值放在该消息的正文中。 创建 HttpResponseMessage 后,将其返回到主管道。 通过任何可能存在的 AuthenticationFilter 传递新创建的 HttpResponseMessage。 HttpResponseMessage 流经 HttpControllerDispatcher,此时它可能不会对它做任何事情。 Response 也流经 HttpRoutingDispatcher,它又不会对它做任何事情。 现在,响应将通过任何设置为处理它的 DelegatingHandlers 进行。此时,DelegatingHandler 对象实际上只能更改正在发送的响应(例如,拦截某些响应并更改为适当的 HTTP 状态)。 最终的 HttpResponseMessage 被提供给 HttpServer 实例。 向调用客户端返回一个 Http 响应

【讨论】:

【参考方案2】:

看源码,ODataController是另一个控制器,继承自 具有自定义路由和格式的 ApiController。所以我猜所有适用于 ApiController 的逻辑 也适用于此。它还具有自定义格式和自定义路由,使用 ODataFormatting 和 ODataRouting

IIS 流水线步骤是什么?

IIS 流水线步骤与任何其他 mvc 控制器相同。本质上,我们拥有构成流水线的所有 httpmodules 和处理程序。更多详细信息可以找到asp.net application lifecycle。从这个角度来看,当一个 mvc 请求到来时,URLRoutingModule、MvcRouteHandler 和 Mvchandler 协同工作以服务于一个 MVC 请求。为下一个问题详细解释。

进入 ASP.NET 控制器区域时如何处理请求?何时应用路由?

一切都以 ODataController 开始。MVC 中的几乎所有内容都是可扩展的(13 extensibility points in asp.net mvc),您可以命名它,并且所有这些点都针对 OData 进行了扩展。例如,从自定义控制器开始,我们有

来自IHttpActionSelector的自定义ODataActionSelector。您可以找到示例实现here IActionValueBinder,示例实现here IContentNegotiator

这样的还有很多。

/// 为支持使用 OData 格式写入和读取数据的 OData 控制器定义一个基类

/// </summary>
[ODataFormatting]
[ODataRouting]
[ApiExplorerSettings(IgnoreApi = true)]
public abstract class ODataController : ApiController
    接收应用程序的第一个请求 -> 在 Global.asax 文件中,Route 对象被添加到 RouteTable 对象中。 执行路由 -> UrlRoutingModule 模块使用 RouteTable 集合中的第一个匹配 Route 对象。从 ODataRouting,路由被添加到 RouteTable 集合中。 创建 MVC 请求处理程序 -> MvcRouteHandler 对象创建 MvcHandler 类的实例并将 RequestContext 实例传递给处理程序 创建控制器 -> MvcHandler 对象使用 RequestContext 实例来识别 IControllerFactory 对象来创建控制器实例 执行控制器 -> MvcHandler 实例调用控制器的 Execute 方法 Invoke action -> 对于从 ControllerBase 类继承的控制器,与控制器关联的 ControllerActionInvoker 对象确定要调用控制器类的哪个 action 方法,然后调用该方法 7.Action返回所有自定义CreatedODataResult、UpdatedODataResult等 为 ODATA 注册了自定义 ODataMediaTypeFormatter 来格式化数据。

【讨论】:

以上是关于WebApi 2 中 OData 控制器 http 请求的完整生命周期是啥的主要内容,如果未能解决你的问题,请参考以下文章

ODATA WEB API----ODATA服务与客户端

System.Web.Http 中的 OData SingleResult 不明确

对 DTO 的 ASP.NET WebApi OData 支持

Web API 中 OData POST 的媒体资源支持

WebAPI 2.2 中没有命名空间的 OData v4 自定义函数

OData v4 WebAPI 响应中的项目计数