0

Background

I have a controller

public class WorkOrderController : ApiController
{
        // GET: api/WorkOrder
        public IEnumerable<WhateverObj> Get()
        {
            //etc..
        }

        // GET: api/WorkOrder/123
        public WhateverObj Get(string id)
        {
           //etc..
        }

        // GET: api/WorkOrder/5/020
        public WhateverObj Get(string id, string opID)
        {
           //etc...
        }
}

and the following routes:

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

config.Routes.MapHttpRoute(
    name: "ServicesApi",
    routeTemplate: "api/{controller}/{id}/{opID}",
    defaults: new { opID = RouteParameter.Optional }
);

This works as expected, I can navigate to the above example URLs.

The Problem

Now i want to create another Controller with only 1 method as follows:

public class FilteredWorkOrderController : ApiController
{

        //By WorkCentreID = ABC, XYZ, UVW
        public IEnumerable<WhateverObj> Get(string workCentreID)
        {
        //etc...
        }
}

The following URL hits the above method ok.

http://localhost:62793/api/FilteredWorkOrder/?workCentreID=ABC

But the (alternative) form

http://localhost:62793/api/FilteredWorkOrder/ABC

does not work, error message is:

{"Message":"No HTTP resource was found that matches the request URI 'http://localhost:62793/api/FilteredWorkOrder/ABC'.","MessageDetail":"No action was found on the controller 'FilteredWorkOrder' that matches the request."}

What route mapping configuration do I need, to get the alternative URI to also work?

I tried

    config.Routes.MapHttpRoute(
        name: "FilteredApi",
        routeTemplate: "api/{controller}/{workCentreID}"
    );

but this does NOT work.

I've noticed that in the Filtered controller, if I change my parameter name in Get(string workCenterID) to Get(string id), then both URLs work!

http://localhost:62793/api/FilteredWorkOrder/?id=ABC
http://localhost:62793/api/FilteredWorkOrder/ABC

What is so magical about the parameter name: 'id'?

I want my parameter to be called workCentreID.

2
  • 1
    Have you looked into Attribute routing? Its what I tend to use these days, as it keeps the routing close to the code effected. learn.microsoft.com/en-us/aspnet/web-api/overview/… Commented Feb 6, 2018 at 15:24
  • Check the order of your routes. more general routs should come after more targeted or specialized routes otherwise the general routes will catch and handle requests meant for other routes. So in your case you need to switch the order of the routes currently registered or another option would be to consider using attribute routing Reference Attribute Routing in ASP.NET Web API 2 Commented Feb 6, 2018 at 16:50

3 Answers 3

2

1) Parameter names in route template should match your action's arguments names. This is a convention. So, when you have registered the default route in config:

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

and try to request http://localhost:62793/api/FilteredWorkOrder/ABC - application can't find an action, because there are no pattern matches.

2) The order, in which routes are registered in your config, matters. If you have more than one potential pattern matches, the engine will choose the first one. After changes, you are describing in second case, the engine looks in routetable and matches again the default route for url http://localhost:62793/api/FilteredWorkOrder/ABC - which doesn't correlate with action signature.

3) For second case - if you will place your custom route registration before default route - your URL should work:

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

Besides, take a look at this article, describing routing in WEB API

Sign up to request clarification or add additional context in comments.

1 Comment

I will read the article, thanks; Putting the FilteredApi first works BUT breaks something else; the URL api/WorkOrder/123 always ends up hitting the Get() method as if i am requesting api/WorkOrder; it cannot see the 'id' parameter
0

The comments / answers pointed me in the right direction to use attribute-based routing. What i needed was

public class FilteredWorkOrderController : ApiController
{
    //By WorkCentreID = ABC, XYZ, UVW
    [Route("api/WorkOrders/{workCentreID}")]
    public IEnumerable<WhateverObj> Get(string workCentreID)
    {
        //etc...
    }

}

and i can make a request with http://localhost:62793/api/WorkOrders/ABC

However, it appears with attribute-based routing, the alternative form does not work, that is, i CANNOT make a request using:

http://localhost:62793/api/WorkOrders/?workCentreID=ABC

Comments

0

I'd suggest you use route attributes. Why?

  1. It never brings you any troubles like patterns you described.
  2. It's really more readable than patterns.

    [Route("{workCentreID}")]
    public IEnumerable<WhateverObj> Get(string workCentreID)
    {
    //etc...
    }
    

2 Comments

Attribute routing solved my problem. But i cannot accept your answer as the syntax is wrong, for example, workcentreID is a parameter, should be in curly brackets; does [FromRoute] really exist ? which Class contains it, what do i need to reference.
@joedotnot yeah, you're right. If you use WebApi 2 there's no [FromRoute] attribute, because it's appeared from asp.net core. I fixed the answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.