HttpGetAttribute 的构造函数中的 "name" 属性 是什么?

What is "name" property in the constructor for HttpGetAttribute?

当我使用 HttpGet(...) 时,intellisense 告诉我除了第一个参数,即 pattern,我还有名称订单。虽然后者对我来说很明显,但我有点不确定参数 name 的用途。

查看文档,我看到 constructor of HttpGet only declares a single parameter. That confused me and I suspect that I'm missing something or using the Framework's version 而不是 Core 或其他东西。

Name 是路由名称,与模板不同。见 docs page.

文档状态:

Route names can be used to generate a URL based on a specific route. Route names:

  • Have no impact on the URL matching behavior of routing.
  • Are only used for URL generation.

Route names must be unique application-wide.

您可以使用名称为使用 IUrlHelper 的命名路由生成 URL。例如,如果您将一条路线命名为“Konrad”,您可以像这样生成一个 link:

string url = urlHelper.Link("Konrad", new { id = 5, query = "test" });

其中id和query为路由参数

顺便说一下,您遇到的文档问题是 HttpGetAttribute。属性语法允许您在任何位置构造函数值之后按名称指定属性的属性值。

考虑以下属性,您可以看到构造函数接受 int a,但还有一个字符串 属性:B.

public class TestAttribute : System.Attribute
{
    public TestAttribute(int a)
    {
    }

    public string B {get;set;}
}

要使用这样的属性,您可以通过以下方式应用它:

[TestAttribute(5)] // B is null
[TestAttribute(5, B = "hello")] // B is "hello"

或简称为 Test:

[Test(5)] // B is null
[Test(5, B = "hello")] // B is "hello"

正如我所看到的 HttpMethodAttributeName 属性 的最大优势(这是 HttpGetAttribute 的基础 class)是你可以区分方法重载:

[HttpGet(Name="ById"]
public IActionResult GetBy(int id)
{

}

[HttpGet(Name="ByExternalId"]
public IActionResult GetBy(Guid id)
{

}  

所以,这对路由选择有帮助。


编辑:我修改了答案

上面的示例代码将导致 AmbiguousMatchException,其中说明相同的 Template 已注册用于不同的操作。

我已经整理了另一个样本并使用以下 RouteDebugger 来获得洞察力。在 Configure 方法中,我调用了 app.UseRouteDebugger() 方法,以便能够在 /route-debugger 下以 json 格式查看已注册的路由url.

[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase
{
    [HttpGet()] 
    public IActionResult GetA(string a)
    {
        return Ok(nameof(GetA));
    }

    [HttpGet(template: "GetB")]
    public IActionResult GetB(string b)
    {
        return Ok(nameof(GetB));
    }

    [HttpGet(template: "GetC", Name= "GetD")]
    public IActionResult GetD(string d, string e)
    
    {
        return CreatedAtRoute(routeName:"GetC", routeValues: new { c = "v"}, value: null);
    }

    [HttpGet(template: "GetC/{c}", Name = "GetC")]
    public IActionResult GetC(string c)
    {
        return Ok(nameof(GetC));
    }
}

路线 table 看起来像这样:

[
   {
      "Action":"GetA",
      "Controller":"Test",
      "Name":null,
      "Template":"api/Test",
      "Contraint":[{}]
   },
   {
      "Action":"GetB",
      "Controller":"Test",
      "Name":null,
      "Template":"api/Test/GetB",
      "Contraint":[{}]
   },
   {
      "Action":"GetD",
      "Controller":"Test",
      "Name":"GetD",
      "Template":"api/Test/GetC",
      "Contraint":[{}]
   },
   {
      "Action":"GetC",
      "Controller":"Test",
      "Name":"GetC",
      "Template":"api/Test/GetC/{c}",
      "Contraint":[{}]
   }
]

如您所见,发生了以下情况:

GetA 方法

  • 因为没有指定Template所以暴露在controller路由下
  • 路由本身没有 Name,因此您不能通过 ActionLinkCreatedAtRoute 等中的名称引用此路由

GetB 方法

  • 它暴露在 api/test/getb 下,因为控制器和操作的 Template 属性组合在一起。
  • 路由本身没有 Name,因此您不能通过 ActionLinkCreatedAtRoute 等中的名称引用此路由

GetC 方法

  • 它暴露在 api/test/getc/{c} 下,因为控制器和操作的 Template 属性组合在一起。 c 参数可以接受任何值。如果未提供,则将调用 GetD,因为它已先注册。
  • 这条路线有一个 Name (GetC) 所以你可以通过它在 ActionLinkCreatedAtRoute 中的名称来引用这条路线,等等。就像我们一样它在 GetD
  • 里面

GetD 方法

  • 它暴露在 api/test/getc 下,因为控制器和操作的 Template 属性组合在一起。因为它是在 GetC 方法的路由之前注册的,所以如果没有提供进一步的路径,它将被调用。
  • 该路线有 Name (GetD)。在 CreatedAtRoute 中,我们通过名称而不是路由来引用 GetC。如果我们将 Name 替换为 GetC 那么它将在运行时抛出以下异常:

InvalidOperationException: The following errors occurred with attribute routing information:

Error 1: Attribute routes with the same name 'GetC' must have the same template: Action: 'Controllers.TestController.GetD ()' - Template: 'api/Test/GetC' Action: 'Controllers.TestController.GetC ()' - Template: 'api/Test/GetC/{c}

总结

  1. 如果您使用相同的 Template 注册两条路由,当您对该路由进行调用时,它将在运行时抛出 AmbiguousMatchException。 (因此,其他路线也可以正常工作)
  2. 如果您使用相同的 Name 注册两个路由,当您进行任何调用时,它将在运行时抛出一个 InvalidOperationException。 (所以,其他路线不行)
  3. 路线的 Name 提供了在不知道确切 Template 的情况下轻松引用它们的能力。这种分离使我们能够在不影响引用链接的情况下更改 Template

我认为 this and this 可以提供帮助。

路线名称

下面的代码定义了一个路由名称Products_List:

[ApiController]
public class Products2ApiController : ControllerBase
{
    [HttpGet("/products2/{id}", Name = "Products_List")]
    public IActionResult GetProduct(int id)
    {
        return ControllerContext.MyDisplayRouteInfo(id);
    }
}

路线名称可用于根据特定路线生成 URL。路线名称:

对路由的URL匹配行为没有影响。
仅用于 URL 代。
路由名称在应用程序范围内必须是唯一的。

将上述代码与常规默认路由进行对比,默认路由将id参数定义为可选({id?})。精确指定 API 的能力具有优势,例如允许 /products/products/5 被分派到不同的操作。

在 asp.net 核心源代码中我只找到 this usage

[HttpGet("{id}", Name = "FindPetById")]
[ProducesResponseType(typeof(Pet),  StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<Pet>> FindById(int id)
{
     var pet = await DbContext.Pets
                .Include(p => p.Category)
                .Include(p => p.Images)
                .Include(p => p.Tags)
                .FirstOrDefaultAsync(p => p.Id == id);
     if (pet == null)
     {
         return new NotFoundResult();
     }

     return pet;
}

它说它们可用于根据特定路线生成 URL。我不认为我理解它的意思(尽管理解这些词)。请详细说明

这是我的两分钱:

在设计适当的 API 时,成熟度模型的第 3 级谈到 HATEOAS 约束(超媒体作为应用程序状态的传输引擎)。有关它的更多信息,请访问:Maturity models of Rest API

为了遍历资源并了解该资源可用的操作,我们为该资源生成 links。

links 的生成是在 URLHelper 库的帮助下完成的,它接受一个名称参数来定义 link。我们在 URLHelper 库中关联的名称是使用属性名称参数为 HTTP GET/POST/PUT 等操作定义的名称。

简单来说,它是您的路线的标识符。

P.S

可在 github:

上找到带有 HATEOAS 的网络 api 的完整工作示例

Web Api with HATEOAS example