2.2WebApi路由在Action上

时间:2022-05-22 01:56:09

这篇文章描述 ASP.NET Web API 如何将 HTTP 请求路由到特定的操作在控制器上。

有关路由的高级别概述,请参见ASP.NET Web API 的路由.

本文着眼于路由进程的详细信息。如果你创建一个 Web API 项目并发现一些请求不要路由你期望的方式,希望这篇文章将帮助。

路由具有三个主要阶段 ︰

  1. 匹配的 URI 到工艺路线模板。
  2. 选择一个控制器。
  3. 选择操作。

您可以用您自己的自定义行为替换一些零件的过程。在本文中,我描述的默认行为。最后,我注意到的地方,在那里你可以自定义的行为。

工艺路线模板

工艺路线模板看起来类似于 URI 路径,但它可以有占位符值,用花括号表示 ︰

"api/{controller}/public/{category}/{id}"

当你创建一个路由时,您可以为部分或全部的占位符提供默认值 ︰

您还可以提供约束,限制如何 URI 段可以匹配一个占位符 ︰

框架会尝试匹配模板的 URI 路径中的段。在模板中的文本必须完全匹配。占位符匹配任何价值,除非你指定的约束。框架与其他部分的 URI,如主机名或查询参数不匹配。框架将匹配 URI 路由表中选择的第一个路由。

有两个特别占位符:"{控制器}"和"{}"。

  • "{}"控制器提供的控制器的名称。
  • "行动 》 {}"提供的操作的名称。在 Web API 中,通常的约定是省略"的行动 {}"。

默认值

如果您提供默认值,该路由将与匹配 URI,它缺少这些段。例如 ︰

"Http://localhost/api/products"URI 匹配这条路线。"{类别}"段分配的默认值"全部"。

路由字典

如果框架找到了匹配的 URI,它会创建一个字典,包含为每个占位符的值。键是占位符名称,不包括大括号。这些值被从 URI 路径或默认值。这本词典存储在IHttpRouteData对象中。

在此路由匹配的阶段,特别"{}"控制器和"的行动 {}"占位符治疗就像其他占位符。它们只存储在字典中,其他值。

默认情况下可以有RouteParameter.Optional的特殊价值。如果占位符获取指定此值,值不是添加到路由字典。例如 ︰

对于"api/产品"的 URI 路径,路由字典将包含 ︰

  • 控制器:"产品"
  • 类别:"所有人"

然而,对于"api/产品/玩具/123",路由字典将包含 ︰

  • 控制器:"产品"
  • 类别:"玩具"
  • id:"123"

默认设置也可以在路线模板中包含一个值未在任何地方出现。如果该路由匹配,该值存储在字典中。例如 ︰

如果 URI 路径是"api/根/8",这本字典将包含两个值 ︰

  • 控制器:"客户"
  • id:"8"

选择控制器

IHttpControllerSelector.SelectController方法处理控制器所选内容。此方法采用HttpRequestMessage的实例,并返回HttpControllerDescriptor。由DefaultHttpControllerSelector类提供默认实现。此类使用一种简单的算法 ︰

  1. 在路由字典键"控制器"中查找。
  2. 采取此键的值和追加字符串"控制器",得到控制器的类型名称。
  3. 寻找与此类型名称的 Web API 控制器。

例如,如果路由字典包含键-值对"控制器"="产品",然后是控制器的类型是"ProductsController"。如果没有匹配的类型或多个匹配项,框架向客户端返回错误。

步骤 3, DefaultHttpControllerSelector使用IHttpControllerTypeResolver接口获取 Web API 控制器类型的列表。IHttpControllerTypeResolver的默认实现返回所有的公共类的 (a) 执行IHttpController,(b) 是不抽象,和 (c) 有一个名称,在"控制器"中结束。

选择动作

选择控制器后, 框架通过调用IHttpActionSelector.SelectAction方法将选择的行动。此方法采用HttpControllerContext并返回HttpActionDescriptor.

ApiControllerActionSelector类提供默认实现。若要选择一个动作,它看起来在以下 ︰

  • 请求的 HTTP 方法。
  • 在工艺路线模板中,如果存在"的行动 {}"占位符。
  • 在控制器上的操作的参数。

选择算法之前,我们需要了解一些控制器操作的东西。

在控制器上的方法被视为"行动"?当选择某个操作,框架只是看看公共实例方法的控制器上。此外,它排除了"特别的名字"方法 (构造函数、 事件、 运算符重载,等等),和从继承的方法ApiController类。

的 HTTP 方法。框架只选择匹配的要求,确定如下的 HTTP 方法的行为 ︰

  1. 您可以使用属性指定的 HTTP 方法 ︰ AcceptVerbs、 HttpDelete公共、 HttpHead、 HttpOptions、 HttpPatch、 HttpPostHttpPut.
  2. 否则,如果控制器方法的名称以"Get"、"开机自检"、"放"、"删除"、"头"、"选项"或"修补程序"开头,然后由公约行动支持的 HTTP 方法。
  3. 如果以上都不是,该方法支持邮政。

参数绑定。参数绑定是如何 Web API 创建参数的值。参数绑定的默认规则是这样的 ︰

  • 简单类型是采取从 URI。
  • 复杂类型是取自请求正文。

简单类型包括所有的.NET 框架基元类型,加上日期时间十进制、 Guid字符串时间跨度。为每个操作,顶多一个参数可以读取请求正文。

它是可以重写默认绑定规则。请参见WebAPI 参数绑定引擎盖下.

与这种背景下,这里是行动选择算法。

  1. 在匹配的 HTTP 请求方法的控制器上创建的所有操作的列表。
  2. 如果路由字典有的"操作"项,删除其名称与此值不匹配的操作。
  3. 尝试匹配到的 URI,操作参数,如下所示 ︰
    1. 为每个操作,得到是一个简单的类型,其中绑定从 URI 获取参数的参数的列表。排除的可选参数。
    2. 从该列表中,试着找到适合每个参数的名称,或者路由字典或 URI 的查询字符串中。匹配项是区分大小写,并不依赖于参数顺序。
    3. 选择列表中的每个参数在 URI 匹配操作。
    4. 如果更多这一行动符合这些标准,挑选一个有大多数的参数匹配。
  4. 忽略[侵权]属性的行动。

步骤 #3 是最容易混淆的。其基本思想是一个参数可以获取其值,从 URI,从请求正文中,或从自定义绑定。对于来自 URI 的参数,我们想要确保 URI 实际上包含该路径 (通过路由字典) 中或在查询字符串中的参数的值。

例如,请考虑以下行动 ︰

Id参数绑定到的 URI。因此,这一行动只能匹配一个 URI,它包含"身份证号",在路由字典或查询字符串中的值。

可选参数是个例外,因为它们是可选的。为可选的参数,它是确定如果绑定不能从 URI 中获取价值。

复杂类型是个例外,出于不同的原因。复杂类型通过自定义绑定只能绑定到的 URI。但在这种情况下,框架不能预先知道是否该参数会绑定到特定的 URI。为了找到答案,它将需要调用绑定。选择算法的目标是从静态描述,在调用任何绑定之前选择的操作。因此,复杂类型被排除的匹配算法。

选择操作后,所有的参数绑定调用。

摘要 ︰

  • 行动必须匹配请求的 HTTP 方法。
  • 如果目前操作名称必须匹配路由字典中的"行动"条目。
  • 为每个参数的作用,如果该参数是从 URI,然后参数名称必须找到路由字典或 URI 的查询字符串中。(可选参数和复杂类型的参数除外。)
  • 尝试匹配最多的参数。最佳匹配可能是一种不带任何参数的方法。

扩展的示例

路线 ︰

routes.MapHttpRoute(
name: "ApiRoot",
routeTemplate: "api/root/{id}",
defaults: new { controller = "products", id = RouteParameter.Optional }
);
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);

控制器 ︰

public class ProductsController : ApiController
{
public IEnumerable<Product> GetAll() {}
public Product GetById(int id, double version = 1.0) {}
[HttpGet]
public void FindProductsByName(string name) {}
public void Post(Product value) {}
public void Put(int id, Product value) {}
}

HTTP 请求 ︰

GET http://localhost:34701/api/products/1?version=1.5&details=1

路由匹配

URI 与路由匹配的名为"DefaultApi"。路由字典包含以下项 ︰

  • 控制器:"产品"
  • id:"1"

路由字典不包含查询字符串参数、"版本"和"详细信息",但这些仍被视为在行动选择。

控制器所选内容

从路由字典中的"控制器"条目,控制器类型是ProductsController.

选择动作

HTTP 请求是 GET 请求。支持 GET 的控制器行为是GetAll、 GetByIdFindProductsByName。路由字典不包含"行动"的条目,所以我们不需要匹配的操作名称。

接下来,我们尝试匹配参数名称的操作,只看得到的行动。

行动 匹配的参数
GetAll 没有一个
GetById ""id
FindProductsByName "名称"

请注意,不是GetById版本参数,因为它是一个可选的参数。

GetAll方法匹配琐细。GetById方法也相匹配,因为路由字典包含"id"。FindProductsByName方法不匹配。

GetById方法赢了,因为它与一个参数,与GetAll没有参数相匹配。用下面的参数值调用该方法 ︰

  • id = 1
  • 版本= 1.5

请注意,即使在选择算法不使用版本,参数的值来自 URI 的查询字符串。

扩展点

Web API 的某些部件中的路由选择进程提供了扩展点。

为任何这些接口提供自己的实现,请使用HttpConfiguration对象上的服务集合 ︰