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(...)
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 ...
source to share
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 ... **
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
:
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")
}
);
source to share