Web API 路由-API/{ controller }/{ action }/{ id }“功能失调”API/{ controller }/{ id }

我有 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}路线的教程甚至提到了这个问题。

124079 次浏览

The route engine uses the same sequence as you add rules into it. Once it gets the first matched rule, it will stop checking other rules and take this to search for controller and action.

So, you should:

  1. Put your specific rules ahead of your general rules(like default), which means use RouteTable.Routes.MapHttpRoute to map "WithActionApi" first, then "DefaultApi".

  2. Remove the defaults: new { id = System.Web.Http.RouteParameter.Optional } parameter of your "WithActionApi" rule because once id is optional, url like "/api/{part1}/{part2}" will never goes into "DefaultApi".

  3. Add an named action to your "DefaultApi" to tell the route engine which action to enter. Otherwise once you have more than one actions in your controller, the engine won't know which one to use and throws "Multiple actions were found that match the request: ...". Then to make it matches your Get method, use an ActionNameAttribute.

So your route should like this:

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

And your controller:

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

You can solve your problem with help of Attribute routing

Controller

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

URI in jquery

api/category/1

Route Configuration

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.
}
}
}

and your default routing is working as default convention-based routing

Controller

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

URI in Jquery

/api/records/1

Route Configuration

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

Review article for more information Attribute routing and onvention-based routing here & this

Try this.

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 }


);
}
}

The possible reason can also be that you have not inherited Controller from ApiController. Happened with me took a while to understand the same.

To differentiate the routes, try adding a constraint that id must be numeric:

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