I'm using WebAPI in my MVC project and I'm facing an issue where the API routes are being handled by MVC routing. I can get around this by changing the order the routes are registered in e.g:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
// Register API routes first
WebApiConfig.Register(GlobalConfiguration.Configuration);
RouteConfig.RegisterRoutes(RouteTable.Routes);
}
But I was wondering if it's possible to do this via the IgnoreRoute() method, I've tried:
routes.IgnoreRoute("api/{controller}/{*folder}");
But I get a 404 back which seems to indicate that the request is still being handled by MVC routing and not WebAPI as before.
Here is my API route:
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{folder}",
defaults: new { folder = RouteParameter.Optional }
);
Added MVC route:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Info", action = "Index", id = UrlParameter.Optional }
);
Introduce a custom constraint which says that the Controller name cannot be api.
Here is an example based on the MVC documentation
public class NoApiControllerConstraint : IRouteConstraint
{
public bool Match
(
HttpContextBase httpContext,
Route route,
string parameterName,
RouteValueDictionary values,
RouteDirection routeDirection
)
{
return values["controller"] != "api";
}
}
routes.MapRoute(
"Default",
"{controller}/{action}",
new {controller="Home"},
new {isNotForApi=new NoApiControllerConstraint()}
);
Related
I'm using ASP.NET MVC 5
I'm having issues with both routes and parameters.
I have this function in my ControllerBase
[HttpGet]
[Route("~/obtenerAngulos/{Conex_AT}/{Conex_BT}")]
public JsonResult obtenerAngulos(string Conex_AT, string Conex_BT)
{
return Json(
new
{
AT = Conex_AT,
BT = Conex_BT
}
, JsonRequestBehavior.AllowGet);
}
And I start having problems receiving the second parameter Conex_BT the Url.Action() returns this route http://localhost:53645/Base/obtenerAngulos?Conex_AT=Y&Conex_BT=y the problem, is Conex_BT is always null
Then I try to work with route and add the Data Anotation for it [Route("~/obtenerAngulos/{Conex_AT}/{Conex_BT}")] but with Url.Action() I keep getting the same route as before.
Even if I try to write it manually like http://localhost:53645/Base/obtenerAngulos/AA/BB I get
HTTP Error 404.0 - Not Found
I mention both problems because I'm pretty sure they are relationated.
Here is the route configuration
RouteConfig.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Make sure you have enabled attribute routing on the route collection.
//enable attribute routes
routes.MapMvcAttributeRoutes();
//convention-based routes
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
This now means that the following should match obtenerAngulos/y/x
public class ControllerBase: Controller {
//Matches obtenerAngulos/y/x
[HttpGet]
[Route("~/obtenerAngulos/{Conex_AT}/{Conex_BT}")]
public JsonResult obtenerAngulos(string Conex_AT, string Conex_BT) {
//...
}
}
The tilde (~) on the method attribute is used to override any route prefixes if needed.
Routes are matched in the route table in the same order they are added. In your example you had convention based routes registered before attribute routes. Once a route is matched it no longer looks for other matches.
Reference Attribute Routing in ASP.NET MVC 5
I have a web API and i want to add a few asp.net pages to manage aspects of the API.
In the WebApiConfig.cs file i have a couple of routes, with the following being used as my catch all route for the API.
config.Routes.MapHttpRoute(name: "CatchAll",routeTemplate: "{controller}/{action}/{id}",defaults: new { id = RouteParameter.Optional });
I want to add a new section that is prefixed by /control to push to the asp.net side of things. To do this i have added the following to RouteConfig.cs
routes.MapRoute(
name: "Default",
url: "control/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
I have then added a standard controller that i will use to manage things.
public class ManageController : Controller
{
public ActionResult Index()
{
return Content("Works");
}
}
When i visit /control/manage in the browser i get the following error.
No type was found that matches the controller named 'control'
It looks like the route is being completely bypassed or at best, the catch all from the api route is catching it and giving it priority. IS there a way i can make the request catch this route without having to create a separate project?
The order of registration is matter
You need to register
public class RouteConfig {
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "DefaultMVC",
url: "control/{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
//..other routes.
}
}
in the RouteConfig.cs before
config.Routes.MapHttpRoute(name: "CatchAll",routeTemplate: "{controller}/{action}/{id}",defaults: new { id = RouteParameter.Optional });
in the WebApiConfig.cs file.
What happens in your case is that the /control/manage url is handled by CatchAll route thus mapping control to a controller and manage to an action
In order to do that register them in Global.asax in the following order:
RouteConfig.RegisterRoutes(RouteTable.Routes);
GlobalConfiguration.Configure(WebApiConfig.Register);
I have created my first WebAPI project to learn, which had an index.html page in the root of the project. I set that page as Startup. All working fine. But, I want to use an MVC controller to call the View instead.
So I created a new MVC controller in my Controller folder called "DefaultController". In it, there's a method:
public ActionResult Index()
{
return View();
}
I created a View folder, and off that, a Default folder, in which, I created an Index.cshtml file.
When I start the project, it calls my old index.html. So, I changed the startup to be the index.cshtml, which is wrong - know. MVC calls a controller method. So, I'm trying to work out - how do I call the controller method in my DefaultController?
I think I need to change my routes?
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { action = "Index", id = UrlParameter.Optional }
);
}
My plan is to use cshtml pages (instead of html pages) to make use of layouts and allow controllers to initiate the views. Each view will the use an api call back to my WebApi controllers to do the data handling.
Does that seem like a good way to handle my WebAPI/KnockoutJs project?
I just need to know how to get the controller to be the default.
When removing the index.html page, I get the error:
HTTP Error 403.14 - Forbidden The Web server is configured to not list
the contents of this directory.
I think you need to add the controller name to the defaults object as in:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional }
);
}
You need to register both WebAPI routes and MVC routes:
All this should be done in Application_Start method in Global.asax.cs file:
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
GlobalConfiguration.Configure(WebApiConfig.Register) is used to configure WebApi (and register api related routes)():
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Configure Web API to use only bearer token authentication.
config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter(OAuthDefaults.AuthenticationType));
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
RouteConfig.RegisterRoutes(RouteTable.Routes); is used to register MVC routes:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Default", action = "Index", id = UrlParameter.Optional }
);
}
You also need verify that the DefaultController that you created is MVC controller (inherits from System.Web.Mvc.Controller) and not WebAPI controller
According to my experience, when you want to call the index.cshtml, in the route config you have to define the controller like this in the RouteConfig.cs:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
The controller supposed to be "Home", and the Action is "Index". But, this maproute is for default one. How about if you want to add another one?
routes.MapRoute(
"Article",
"articles",
new { controller = "News", action = "ArticleList" }
);
You can write freely as shown above where "Articles" is the name of maproute and the "articles" is the url. And it would become like this (http://www.domain.com/articles) if you compile controller News and Action ArticleList. And "..../articles" is something you replace (No need define controller or action) and you don't need to open www.domain.com/News/ArticleList it's enough to go to url www.domain.com/articles and the maproute would be automaticaly route to controller news and action articlelist.
That's only my point of view about how maproute works.
CMIIW :)
I just merged an existing API project into another existing MVC project. The API controllers have the same name as the MVC controllers but they're in 2 different namespaces (MyApp.Web.MyController and MyApp.API.MyController, respectively).
Now, I don't really know how to configure the routes so that I can access the API controllers :(
I read this post : Mixing Web Api and ASP.Net MVC Pages in One Project and would like the achieve what #Mike Wasson suggested there, but I don't know how to configure the routes.
This is what I currently have in RouteConfig.cs:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
It looks like you already have it working, but should you ever wish to use your API controllers in an area, you can enable it simply by adding an additional route.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultAreaApi",
routeTemplate: "api/{area}/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
// Application_Start
GlobalConfiguration.Configure(WebApiConfig.Register);
AreaRegistration.RegisterAllAreas();
I have never come across this pattern of code right here. Would anyone care to explain it to me? (Or is there even a pattern here? Is there a reason why this was done like this? What benefits is this giving?) I'm new at general programming, and this is a very interesting one to me:
Global.asax.cs
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
//...
RouteConfig.RegisterRoutes(RouteTable.Routes);
//...
}
WebApiConfig.cs
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
RouteConfig.cs
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
You could easily write the code in your sample like this:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional });
RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
RouteTable.Routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new {
controller = "Home",
action = "Index",
id = UrlParameter.Optional });
}
However, as the amount of necessary configuration grows, it makes sense to split it up into several logically related chunks. ASP.NET MVC supports this fairly well, and the default project template is just trying to guide you towards doing so.
This isn't a pattern as much an example of the Single Responsibility Principle (SRP). In Global.asax, we know of the high-level tasks that are required to set things up but we leave the implementation separated.