C# 请求匹配多个端点,但为什么?

C# 请求匹配多个端点,但为什么?,c#,rest,C#,Rest,我有一个控制器,它有多个路由 我正在尝试调用声明为的端点 GET: api/lookupent/2020-03-17T13:28:37.627691 但这导致了这个错误 Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches: Controllers.RecordController.Get (API) Controllers

我有一个控制器,它有多个路由

我正在尝试调用声明为的端点

GET: api/lookupent/2020-03-17T13:28:37.627691
但这导致了这个错误

Microsoft.AspNetCore.Routing.Matching.AmbiguousMatchException: The request matched multiple endpoints. Matches: 

Controllers.RecordController.Get (API)
Controllers.RecordController.GetRecordRegisteredAt (API)
但我不确定我是否理解为什么这是有意义的,因为这段代码

// GET: api/{RecordName}/{id}
[HttpGet("{RecordName}/{id}", Name = "GetRecord")]
public ActionResult Get(string RecordName, long id)


// GET: api/{RecordName}/{timestamp}
[HttpGet("{RecordName}/{timestamp}", Name = "GetRecordRegisteredAt")]
public ActionResult GetRecordRegisteredAt(string RecordName, string timestamp)

为什么输入与这些端点匹配?

您的问题是,控制器对于接收不同参数的两种不同方法具有相同的路由。 让我用一个类似的例子来说明,您可以有如下两种方法:

Get(string entityName, long id)
Get(string entityname, string timestamp)
到目前为止,这是有效的,至少C#没有给你一个错误,因为它是一个参数过载。但是对于控制器,您有一个问题,当aspnet接收到额外的参数时,它不知道将您的请求重定向到哪里。 您可以更改路由,这是一种解决方案

此解决方案还可以将输入映射到复杂类型,否则可用于简单类型

通常,我更喜欢保持相同的名称,并将参数包装在DtoClass上,例如IntDto和StringDto

public class IntDto
{
    public int i { get; set; }
}

public class StringDto
{
    public string i { get; set; }
}
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    public IActionResult Get(IntDto a)
    {
        return new JsonResult(a);
    }

    [HttpGet]
    public IActionResult Get(StringDto i)
    {
        return new JsonResult(i);
    }
}
但是,你还是有错误。为了将您的输入绑定到方法上的特定类型,我创建了一个ModelBinder,在这种情况下,它如下所示(请参阅,我试图从查询字符串中解析参数,但我使用的是鉴别器头,它通常用于客户端和服务器之间的内容协商()

然后您需要创建一个ModelBinderProvider(请参阅,如果我正在尝试绑定其中一种类型,那么我将使用MyModelBinder)

并将其注册到容器中

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers(options =>
        {
            options.ModelBinderProviders.Insert(0, new MyModelBinderProvider());
        });
    }
到目前为止,您还没有解决您遇到的问题,但我们已经很接近了。为了立即执行控制器操作,您需要在请求上传递头类型:application/jsonapplication/myContentType.json。但是为了支持条件逻辑来确定关联的操作方法是否有效对于给定的请求,您可以创建自己的
ActionConstraint
。基本上,这里的想法是使用此属性装饰您的ActionMethod,以限制用户在未通过正确的媒体类型时执行该操作。请参阅下面的代码和使用方法

[AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = true)]
    public class RequestHeaderMatchesMediaTypeAttribute : Attribute, IActionConstraint
    {
        private readonly string[] _mediaTypes;
        private readonly string _requestHeaderToMatch;

        public RequestHeaderMatchesMediaTypeAttribute(string requestHeaderToMatch,
            string[] mediaTypes)
        {
            _requestHeaderToMatch = requestHeaderToMatch;
            _mediaTypes = mediaTypes;
        }

        public RequestHeaderMatchesMediaTypeAttribute(string requestHeaderToMatch,
            string[] mediaTypes, int order)
        {
            _requestHeaderToMatch = requestHeaderToMatch;
            _mediaTypes = mediaTypes;
            Order = order;
        }

        public int Order { get; set; }

        public bool Accept(ActionConstraintContext context)
        {
            var requestHeaders = context.RouteContext.HttpContext.Request.Headers;

            if (!requestHeaders.ContainsKey(_requestHeaderToMatch))
            {
                return false;
            }

            // if one of the media types matches, return true
            foreach (var mediaType in _mediaTypes)
            {
                var mediaTypeMatches = string.Equals(requestHeaders[_requestHeaderToMatch].ToString(),
                    mediaType, StringComparison.OrdinalIgnoreCase);

                if (mediaTypeMatches)
                {
                    return true;
                }
            }

            return false;
        }
    }
这是您最后的零钱:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    [HttpGet]
    [RequestHeaderMatchesMediaTypeAttribute("Accept", new[] { "application/json" })]
    public IActionResult Get(IntDto a)
    {
        return new JsonResult(a);
    }

    [RequestHeaderMatchesMediaTypeAttribute("Accept", new[] { "application/myContentType.json" })]
    [HttpGet]
    public IActionResult Get(StringDto i)
    {
        return new JsonResult(i);
    }
}
现在,如果运行应用程序,则错误消失。但如何传递参数呢 这一个将击中这个方法:

public IActionResult Get(StringDto i)
        {
            return new JsonResult(i);
        }

这一个,另一个:

 public IActionResult Get(IntDto a)
        {
            return new JsonResult(a);
        }


运行它并让我知道

您可以使用路由约束修复此问题

看看

下面是他们的例子:

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

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

对于这两种方法,我有同样的问题:

[HttpPost]
public async Task<IActionResult> PostFoos(IEnumerable<FooModelPostDTO> requests)

[HttpPost]
public async Task<IActionResult> GetFoos(GetRequestDTO request)

您的控制器是否装饰有
[ApiController]
属性?这是否回答了您的问题?您使用同一路由两次……ApiController的行为与MVC控制器不同……它们对路由的解释不同
[Route("users/{id:int}")]
public User GetUserById(int id) { ... }

[Route("users/{name}")]
public User GetUserByName(string name) { ... }
[HttpPost]
public async Task<IActionResult> PostFoos(IEnumerable<FooModelPostDTO> requests)

[HttpPost]
public async Task<IActionResult> GetFoos(GetRequestDTO request)
[Route("api/[controller]/[action]")]    
[ApiController]
public class FoosController : ControllerBase