Web API route ignored and handled by MVC

I can't figure out why the route is being handled by MVC instead of Web API.

The entire route is configured as follows:

configuration.Routes.MapHttpRoute
(
    name: "AdminControllers.TicketingController",
    routeTemplate: "api/company/tickets",
    defaults: new 
    { 
        controller = "Ticketing", 
        id = RouteParameter.Optional, 
        action = "GetTickets" 
    }
);

      

And the API controller looks like this:

public sealed class TicketingController : ApiController
{
    [HttpGet]
    public HttpResponseMessage GetTickets()
    {
        return ControllerContext.Request.CreateResponse(HttpStatusCode.OK);
    }
}

      

I am making an HTTP / GET request for /api/company/tickets

and I am getting the following error:

The controller for the path "/ api / company / tickets" was not found or does not implement IController.

Stack Trace point Exception for ASP.NET MVC (and not the API Web-): System.Web.Mvc.DefaultControllerFactory.GetControllerInstance(RequestContext requestContext, Type controllerType)

.

AFAIK, this happens when you map a route using a non-standard framework (MVC) instead of a Web API extension method HttpConfiguration.Routes.MapHttpRoute

. In my example found above, you will find that I am using the correct resource to register the controller with the web API.

I can confirm that the route is being logged during application startup.

The problem is that the ASP.NET MVC pipeline is processing the route and obviously can't find a controller for the entire route.

What am I doing wrong here?

NOTE. This is ASP.NET Web API 1.x and I cannot use Web API 2.0 (I would like to use attribute routing, yes).

Update: fig. registered routes after calling.MapHttpRoute(...)

enter image description here

Update 2

Believe it or not, it started working when I changed my route config to:

configuration.Routes.MapHttpRoute(
    name: "AdminControllers.TicketingController",
    routeTemplate: "api/company/tickets/{id}",
    defaults: new 
    { 
        controller = "Ticketing", 
        id = RouteParameter.Optional, 
        action = "GetTickets" 
    }
);

      

And the web API action changed to:

[HttpGet]
public HttpResponseMessage GetTickets(int? id)
{
    return ControllerContext.Request.CreateResponse(HttpStatusCode.OK);
}

      

It looks like the route parameter makes the route swap enough to be ignored by MVC and then processed by the Web API pipeline. But I can confirm that there is no other route starting with "api / company /".

Anyway, it works if I give id

(fe /api/company/tickets/11

). Otherwise the MVC pipeline processes the route ...

+3


source to share


1 answer


After looking for solutions, I have a working one.

First of all, I deploy a Web API Controller to the Windows Azure Pack Admin Site (AdminSite). This has been done since ASP.NET MVC 4.0. Anyway, I believe my answer should make sense if you find the same problem that I will cover in any mixed ASP.NET MVC and Web API application.

Windows Azure Pack registers this ASP.NET MVC route {controller}/{action}/{id}

:: PROBLEM!

* Yes, because it is a URL route that can fit into the API URI scheme ... **

enter image description here

So ... what?

At the end of the day, this solution puts Web API routes ahead of MVC. This way ASP.NET will route requests for the first match of some URI route pattern:

// #1 we add the whole routes. One for listing, other for getting
// a single entity (ticket)
configuration.Routes.MapHttpRoute(
    name: "Company.AzurePack.Ticketing.List",
    routeTemplate: "api/company/tickets",
    defaults: new
    {
        controller = "Ticketing",
        action = "GetTickets"
    }
);

configuration.Routes.MapHttpRoute(
    name: "Company.AzurePack.Ticketing.GetOne",
    routeTemplate: "api/company/tickets/{id}",
    defaults: new 
    { 
        controller = "Ticketing", 
        action = "GetTicket" 
    }
);

// #2 we get both added routes from the route table and we create two
// references to them. Later, we remove and re-insert them at the top
// of route table. BINGO!
RouteBase apiRoute1 = RouteTable.Routes[RouteTable.Routes.Count - 1];
RouteBase apiRoute2 = RouteTable.Routes[RouteTable.Routes.Count - 2];
RouteTable.Routes.Remove(apiRoute1);
RouteTable.Routes.Remove(apiRoute2);

RouteTable.Routes.Insert(0, apiRoute1);
RouteTable.Routes.Insert(0, apiRoute2);

      

Wait, web API routes are registered in the route table GlobalConfiguration.Configuration.Routes

...

Yes, but it HttpConfiguration.Routes

also registers its routes with RouteTable.Routes

, and the ASP.NET pipeline works with RouteTable.Routes

. That is, the MVC and WebAPI routes are in the same route table.



In the following screenshot, you will find that the web API routes are of type HttpWebRoute

:

enter image description here

Now these URIs are served by a fancy web API controller:

  • / API / company / tickets
  • / API / companies / tickets / 11

Extension method to make our life even easier!

After verifying that this solution works, I refactored the above example code to an extension method:

public static class HttpRouteCollectionExtensions
{
    public static IHttpRoute MapHttpRouteAt(this HttpRouteCollection routes, int index, string name, string routeTemplate, object defaults = null, object constraints = null, HttpMessageHandler handler = null)
    {
        Contract.Requires(routes != null);

        IHttpRoute route = routes.MapHttpRoute(name, routeTemplate, defaults, constraints, handler);

        RouteBase apiRoute = RouteTable.Routes[RouteTable.Routes.Count - 1];
        RouteTable.Routes.Remove(apiRoute);

        RouteTable.Routes.Insert(index, apiRoute);

        return route;
    }
}

      

... and now I can add routes at the top of the routing table like this:

configuration.Routes.MapHttpRouteAt(
    index: 0,
    name: "sampleRoute",
    routeTemplate: "api/some/path/{name}",
    defaults: new
    {
        controller = "Some",
        action = "SomeAction",
        httpMethod = new HttpMethodConstraint("GET")
    }
);

      

+2


source







All Articles