Jango
Jango

Reputation: 43

HttpControllerSelector not working with Attribute Routing

I'm using a custom HTTP controller selector to version my API.

config.Services.Replace(typeof(IHttpControllerSelector), new NamespaceSelector(config));

Below is my controller with actions:

[RoutePrefix("api/v1/messages")]
public class MessagesController : ApiController
{
    [Route("unreadall")] // api/v1/messages/unreadall
    public IEnumerable<long> UnreadAll()
    {
      // Return value;
    }
    [Route("{type}/unreadall")] // api/v1/messages/{type}/unreadall
    public IEnumerable<long> UnreadAll(string type)
    {
      // Return value;
    }
    [Route("unreadnext")] // api/v1/messages/unreadnext
    public long UnreadNext()
    {
      // Return value;
    }
    [Route("{type/}unreadnext")] // api/v1/messages/{type}/unreadnext
    public long UnreadNext(string type)
    {
      // Return value;
    }
    [Route("{id:long}/markasread")] // api/v1/messages/123/markasread
    [HttpPut]
    public string MarkAsRead(long id)
    {
        // Return value;
    }
    [Route("{id:long}")] // Default Action
    public string Get(long id) // api/v1/messages/123
    {
        // Return value;
    }
    [Route("")] // Default Action
    [HttpPost]
    public long Post(string message) // api/v1/messages
    {
        // Return value;
    }
}

Below is my route config:

config.Routes.MapHttpRoute(
                name: "DefaultApi1",
                routeTemplate: "api/{version}/{controller}/{id}/{action}"
            );

config.Routes.MapHttpRoute(
                name: "DefaultApi2",
                routeTemplate: "api/{version}/{controller}/{action}"
            );

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

When I test my routes, the following work.

/api/v1/messages/unreadall
/api/v1/messages/unreadnext
/api/v1/messages/123/markasread

But the below routes, also point to the same actions.

/api/v1/messages/type/unreadall
/api/v1/messages/type/unreadnext

And I get errors for the rest of my routes.

/api/v1/messages/123
Error:
{
    "Message": "No HTTP resource was found that matches the request URI 'http://localhost:59411/api/v1/messages/123'.",
    "MessageDetail": "No action was found on the controller 'MessagesController' that matches the name '123'."
}

POST: /api/v1/messages
Error:
{
    "Message": "The requested resource does not support http method 'POST'."
}

Can someone please tell what I'm doing wrong with my route configuration ? or can someone please post working route configuration for my scenarios above ?

Appreciate your help !

Cheers,

Upvotes: 4

Views: 2615

Answers (1)

Mihai Dinculescu
Mihai Dinculescu

Reputation: 20033

What you are getting it's the expected behavior: the routes defined in route config works, while the attribute routes does not.

This happens because request.GetRouteData() is not taking into account attribute routes. This makes sense of course because there is no specific controller that a route points to as attributed routes are related to methods, not controllers.

When you use attribute routing, all the route attributes get added to a common route without a name. This is a special route that is an instance of an internal class called RouteCollectionRoute. This route has a collection of sub-routes that you can query for that includes all the attribute routes. But if you just want the selected route for your call, you can simple ask for it using the RouteData.Values:

var routeData = request.GetRouteData();
var subroutes = (IEnumerable<IHttpRouteData>)routeData.Values["MS_SubRoutes"];
var route = subroutes.First().Route;

Source: http://wildermuth.com/2013/11/12/Web_API_2_s_Attribute_Routing_Looking_Deeper

Upvotes: 3

Related Questions