【问题标题】:The request matched multiple endpoints on .NET Core该请求与 .NET Core 上的多个端点匹配
【发布时间】:2020-10-05 13:51:14
【问题描述】:

我在 .NET Core 项目中使用 OpenAPI (Swagger),当使用具有相似获取请求的多个方法时,我遇到“Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException:请求匹配多个端点。”运行时出错。我查看了网络和 SO 上的几个页面,并尝试应用The request matched multiple endpoints but why? 之类的解决方法,但它不能解决问题。以下是我使用的 API 方法和路由定义。

[Route("get", Name="get")]
public IEnumerable<DemoDto> Get()
{
    //
}

[Route("get/{id}", Name="getById")]
public DemoDto GetById(int id)
{
    //
}

[Route("get/{query}", Name="getWithPagination")]
public IEnumerable<DemoDto> GetWithPagination(DemoQuery query)
{
    //
}

我使用Name 属性来解决问题但没有解决。有什么想法可以更改路线以区分Get()GetWithPagination()

【问题讨论】:

  • @PavelAnikhouski 您问这些问题是为了了解问题并提供帮助吗?
  • 我可以用[HttpGet] 代替[Route("get/{query}", Name="getWithPagination")]吗?
  • @Jack,在 ASP.NET Core 中,我们使用HttpGetAttribute 来定义来自方法的路由。
  • @Vernou 那你建议使用[HttpGet("get/{query}", Name="getWithPagination")]?

标签: c# api rest .net-core webapi


【解决方案1】:

[Route("get/{query}", Name="getWithPagination")]

这没有意义。 DemoQuery 是一个对象,它不能由 url 的单个部分表示。不过,您可以告诉 ModelBinder 从多个查询参数构建您的对象。

路由引擎将此路由与[Route("get/{id}", Name="getById")] 路由混淆。它们似乎都匹配get/blah

除了修复DemoQuery 路由之外,尝试在id 路由上添加路由约束 -

[Route("get/{id:int}", Name="getById")]

更好地帮助引擎。


要让DemoQuery 工作,假设它看起来像:

public class DemoQuery
{ 
     public string Name { get; set; }
     public int Value { get; set; }
}

然后将您的操作更改为

[Route("getPaged/{query}", Name="getWithPagination")]
public IEnumerable<DemoDto> GetWithPagination([FromQuery] DemoQuery query)

然后调用端点,如/getPaged?name=test&amp;value=123。并且 ModelBinder 应该为您构建您的对象。

【讨论】:

  • 是的,这正是我目前所发现的,并通过在this 链接上的应用解决了问题。我使用[Route("get/{query}", Name="getWithPagination")][Route("get/{id:int}", Name="getById")]。但是,我想知道我是否可以使用,但我想知道我是否可以通过[HttpGet] 使用它?我的意思是像[HttpGet("get/{query}", Name="getWithPagination")]?
  • @Jonesopolis 那应该是FromQuery
  • 谢谢@JohnH。凭记忆做到这一点
  • 实际上我完全按照您在您的答案中给出的方式使用,但为了简洁起见,只是想省略。非常感谢,投了赞成票。
  • @Jack 我从不使用 [Route]。我总是在 [HttpXXX] 中定义路线,但这是个人喜好。
【解决方案2】:

您有两个具有相等路由的端点: get/{id} 和 get/{query}。

如果你在浏览器中写:get/123,系统无法理解使用什么路由,因为它们具有相同的模式。

你需要区分它们,我建议你使用restful风格的路线,比如: 项目/{id}, 项目?{您的查询}

【讨论】:

  • 是的,当然。 [HttpGet("Route")] 实际上是 [HttpGet][Route("Route")] 的语法糖
  • 注意,即使你写 [Route("get/{query}", Name="getWithPagination")] - 默认方法仍然是隐式 GET
  • 使用属性是个好习惯,但我建议你不要在路由中写“get, put, post”。这些情况有一个宁静的标准,例如,您的实体是用户。 [Post("users")] 和用户模型在正文中 - 创建用户,[Put("users/{id}")] 和用户模型在正文中 - 更新用户,[Delete("users/{id}" )] - 删除用户,[Get("users/{id}")] - 通过 id 获取用户,[Get("users?{your query}")] - 获取和过滤用户。 2)“名称”在特定情况下使用,但在您的情况下似乎没用
  • 好吧,如果控制器中的所有方法都以共享部分(如“employees”)开头,您可以将此部分移动到控制器级别,如[Route("api/[controller]")]。然后你不要在动作方法中写“员工”这个词,它会自动开始。一个小小的说明,我更喜欢明确指定控制器上方的路由 [Route("api/employees")] (注意,这是复数)。
  • 在有薪水的情况下,路由可能是这样的:Route["employees/{id}/salary"] 或者如果“employees”部分在控制器之上,那么只是 Route["{id}/薪水”]
【解决方案3】:

ASP.NET Web API 2 支持一种新型路由。 Offical Doc

路由约束允许您限制参数类型并匹配这些类型(int、string、甚至日期等)。一般语法是“{parameter:constraint}”

[Route("users/{id:int}")]
public User GetUserById(int id) { ... }

[Route("users/{name}")]
public User GetUserByName(string name) { ... }

我在 API 测试过;

//match : api/users/1 
[HttpGet("{id:int}")]
public IActionResult GetUserById(int id){ ... }

//match : api/users/gokhan
[HttpGet("{name}")]
public IActionResult GetUserByName(string name){ ... }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-11-24
    • 2023-02-03
    • 2020-04-04
    • 1970-01-01
    • 1970-01-01
    • 2021-09-02
    • 2023-01-27
    • 1970-01-01
    相关资源
    最近更新 更多