Web API路由 - api / {controller} / {action} / {id}“dysfunctions”api / {controller} / {id}

问题描述 投票:92回答:4

我在Global.asax中有默认路由:

 RouteTable.Routes.MapHttpRoute(
         name: "DefaultApi",
         routeTemplate: "api/{controller}/{id}",
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );

我希望能够定位特定的功能,所以我创建了另一条路线:

RouteTable.Routes.MapHttpRoute(
         name: "WithActionApi",
         routeTemplate: "api/{controller}/{action}/{id}",
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );

所以,在我的控制器中,我有:

    public string Get(int id)
    {
        return "object of id id";
    }        

    [HttpGet]
    public IEnumerable<string> ByCategoryId(int id)
    {
        return new string[] { "byCategory1", "byCategory2" };
    }

打电话给.../api/records/bycategoryid/5会给我我想要的东西。但是,调用.../api/records/1会给我错误

找到了与请求匹配的多个操作:...

我理解为什么 - 路由只定义了哪些URL是有效的,但是当涉及到函数匹配时,Get(int id)ByCategoryId(int id)都匹配api/{controller}/{id},这就是混淆框架的原因。

我需要做些什么才能让默认API路由再次工作,并保持{action}的路由?我想创建一个名为RecordByCategoryIdController的不同控制器来匹配默认的API路由,我会请求.../api/recordbycategoryid/5。但是,我发现这是一个“脏”(因此不令人满意)的解决方案。我已经找到了这方面的答案,没有关于使用{action}路线的教程甚至提到了这个问题。

asp.net routing controller asp.net-web-api action
4个回答
101
投票

路由引擎使用与向其中添加规则相同的顺序。一旦获得第一个匹配的规则,它将停止检查其他规则并采取此搜索控制器和操作。

所以,你应该:

  1. 将您的特定规则置于一般规则之前(如默认值),这意味着首先使用RouteTable.Routes.MapHttpRoute映射“WithActionApi”,然后使用“DefaultApi”。
  2. 删除“WithActionApi”规则的defaults: new { id = System.Web.Http.RouteParameter.Optional }参数,因为一旦id是可选的,像“/ api / {part1} / {part2}”这样的url将永远不会进入“DefaultApi”。
  3. 将命名操作添加到“DefaultApi”以告知路由引擎要输入的操作。否则,一旦您的控制器中有多个操作,引擎就不会知道要使用哪个操作,并抛出“找到了与请求匹配的多个操作:...”。然后,为了使它与您的Get方法匹配,请使用ActionNameAttribute

所以你的路线应该是这样的:

// Map this rule first
RouteTable.Routes.MapRoute(
     "WithActionApi",
     "api/{controller}/{action}/{id}"
 );

RouteTable.Routes.MapRoute(
    "DefaultApi",
    "api/{controller}/{id}",
    new { action="DefaultAction", id = System.Web.Http.RouteParameter.Optional }
);

而你的控制器:

[ActionName("DefaultAction")] //Map Action and you can name your method with any text
public string Get(int id)
{
    return "object of id id";
}        

[HttpGet]
public IEnumerable<string> ByCategoryId(int id)
{
    return new string[] { "byCategory1", "byCategory2" };
}

36
投票

您可以借助属性路由解决您的问题

调节器

[Route("api/category/{categoryId}")]
public IEnumerable<Order> GetCategoryId(int categoryId) { ... }

jquery中的URI

api/category/1

路线配置

using System.Web.Http;

namespace WebApplication
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            // Other Web API configuration not shown.
        }
    }
}

并且您的默认路由作为默认的基于约定的路由

调节器

public string Get(int id)
    {
        return "object of id id";
    }   

Jquery中的URI

/api/records/1 

路线配置

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Attribute routing.
        config.MapHttpAttributeRoutes();

        // Convention-based routing.
        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{id}",
            defaults: new { id = RouteParameter.Optional }
        );
    }
}

有关更多信息,请参阅文章Attribute routing and onvention-based routing herethis


0
投票

试试这个。

public class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Web API configuration and services

        var json = config.Formatters.JsonFormatter;
        json.SupportedMediaTypes.Add(new System.Net.Http.Headers.MediaTypeHeaderValue("application/json"));
        config.Formatters.Remove(config.Formatters.XmlFormatter);

        // Web API routes
        config.MapHttpAttributeRoutes();

        config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional , Action =RouteParameter.Optional }

        );
    }
}

0
投票

可能的原因还可能是您没有从ApiController继承Controller。我发生了一段时间才明白同样的事情。


0
投票

要区分路由,请尝试添加id必须为数字的约束:

RouteTable.Routes.MapHttpRoute(
         name: "DefaultApi",
         routeTemplate: "api/{controller}/{id}",
         constraints: new { id = @"\d+" }, // Only matches if "id" is one or more digits.
         defaults: new { id = System.Web.Http.RouteParameter.Optional }
         );  
© www.soinside.com 2019 - 2024. All rights reserved.