joedotnot
joedotnot

Reputation: 5153

How to define WebAPI route in asp.net

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.

Upvotes: 0

Views: 351

Answers (3)

cortisol
cortisol

Reputation: 363

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

Upvotes: 0

joedotnot
joedotnot

Reputation: 5153

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

Upvotes: 0

n.prokhorov
n.prokhorov

Reputation: 930

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

Upvotes: 2

Related Questions