Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
347 views
in Technique[技术] by (71.8m points)

asp.net - Web API Routing - api/{controller}/{action}/{id} "dysfunctions" api/{controller}/{id}

I have the default Route in Global.asax:

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

I wanted to be able to target a specific function, so I created another route:

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

So, in my controller, I have:

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

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

Calling .../api/records/bycategoryid/5 will give me what I want. However, calling .../api/records/1 will give me the error

Multiple actions were found that match the request: ...

I understand why that is - the routes just define what URLs are valid, but when it comes to function matching, both Get(int id) and ByCategoryId(int id) match api/{controller}/{id}, which is what confuses the framework.

What do I need to do to get the default API route to work again, and keep the one with {action}? I thought of creating a different controller named RecordByCategoryIdController to match the default API route, for which I would request .../api/recordbycategoryid/5. However, I find that to be a "dirty" (thus unsatisfactory) solution. I've looked for answers on this and no tutorial out there on using a route with {action} even mentions this issue.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...