Web API路由-api / {controller} / {action} / {id}“功能异常” api / {controller} / {id}


94

我在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}甚至提到该问题的教程。

Answers:


104

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

因此,您应该:

  1. 将您的特定规则放在一般规则(例如默认值)之前,这意味着RouteTable.Routes.MapHttpRoute先映射“ WithActionApi”,然后再映射“ DefaultApi”。

  2. 删除defaults: new { id = System.Web.Http.RouteParameter.Optional }“ WithActionApi”规则的参数,因为一旦id是可选的,“ / api / {part1} / {part2}”之类的网址就永远不会进入“ 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" };
}

1
我尝试了上面的建议,一切正常。非常感谢您的“秘密”。
Mickael Caruso,2012年

在答案的第2点,如果id是可选的,则在找不到路由的匹配操作的情况下,类似URL的URL /api/{part1}/{part2}仍会进入路由。如果我错了,请纠正我。DefaultApiWithActionApi
orad 2015年

如果我只想使用api / {controller} / {action},会发生什么情况。没有ID。如果其他人也面临同样的问题。忘记WebApiConfig。阅读有关属性路由的信息。
ahsant

40

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

控制者

[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 }
        );
    }
}

有关更多信息,评论文章属性路由和年公约,在此基础路由


上帝回答。但是我们不能全部添加api / {controller} / {action} / {id}以及api / {controller} / {id}吗?
karim 2014年

通过属性路由可以彻底解决此问题。重要一点:在Web API 2之前,Web API项目模板生成的代码如下:protected void Application_Start(){WebApiConfig.Register(GlobalConfiguration.Configuration); }如果启用了属性路由,则此代码将引发异常。如果要升级现有的Web API项目以使用属性路由,请确保将此配置代码更新为以下内容:protected void Application_Start(){GlobalConfiguration.Configure(WebApiConfig.Register); }
Deeb 2015年

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 }
         );  
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.