How to ignore a route in MVC4 WebAPI configuration? - asp.net-mvc

I have an MVC4 project with elmah added. My global.asax's Application_Start() has
WebApiConfig.Register(GlobalConfiguration.Configuration); // #1
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes); // #2
#1 and #2 are as follows
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional } );
}
...
}
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 }
);
}
}
The templates are identical and routing into the controllers works exactly as we want it (from a URI spec perspective). The issue is that the ignore route is added AFTER the WebAPI route is added. So what should be ignored by MVC4s routing and handled by Elmah (eg /elmah.axd/styles) is instead intercepted by WebAPI and the request fails => so I have no CSS in my elmah.axd pages. I tried flipping #1 and #2 in global.asax but that caused all WebAPI routing to fail - FAR worse than CSS not working in Elmah!
I basically need some way to instruct WebAPI's routing to ignore {resource}.axd/{*pathInfo} right as the first route - how can I do that?

This is what worked for us - moving the ignore out of the wrapper and as the first one.
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
//ignore route first
RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
// And taken out of the call below
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}

Sounds like you need finer control of the order of the route definitions. Instead of pulling these in from the individual RouteConfig and WebApiConfig classes, you could define these directly in global.asax.cs like so:
RouteTable.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
GlobalConfiguration.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "{controller}/{action}/{id}",
defaults: new {id = RouteParameter.Optional});
RouteTable.Routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

Related

Attribute Routing over Convention-based Routing in MVC, which one would be called first?

I am assuming that the convention routing would be added to the route table first because it is getting registered in the global.asax file like this
RouteConfig.RegisterRoutes(RouteTable.Routes);
Now I have a route like this in the route.config
public static void RegisterRoutes(RouteCollection routes)
{
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapMvcAttributeRoutes();
}
and I have an attribute route like this
[Route("students/{id?}")]
public ActionResult Index(int? id)
{
return View(id);
}
Now when I use the URL
localhost:4200//students
The students route is getting called successfully but when I use the route like this
localhost:4200//students/40
I get error, I don't know the reason why. When I remove the route from RouteConfig class I am able to call it successfully.
Can anyone explain me why and how?
In your original example the URL localhost:4200//students/40 is being matched to the url: "{controller}/{action}/{id}", convention-based route template.
But since there is no action called 40 it will fail.
Now because it has already matched a route, it will not check any further for other matches so you will end up with Not Found error.
In Asp.Net MVC, routes are called in the order they are added to the routing table. First match wins and it does not do any further checks.
Targeted attribute routes are usually added before more general convention based routes to avoid route conflicts like the one experienced in your example.
public static void RegisterRoutes(RouteCollection routes) {
//Attribute routes
routes.MapMvcAttributeRoutes();
//Convention-based routes.
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}

MVC.NET Routing

I want to add a new route to my Web Api, which will read various ids and then filter a bunch of books.
So the final url should read something like http://localhost/api/books/1/1/1/1
Now I have added a route to my RouteConfig as follows :-
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "BookFilter",
url: "api/books/{author}/{title}/{genre}/{isbn}",
defaults: new { controller = "Books", action = "BookFilter" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
I also added the following in my BooksController:-
[HttpGet]
public IQueryable<BookDTO> BookFilter(int authorId, int titleId, int genreId, int isbn)
{
//filter books here
return db.Books.ProjectTo<BookDTO>();
}
However when I try to reach the page, I get a 404.
What do I need to do to reach my page?
Thanks for your help and time
Web API and MVC are independent frameworks which each have separate types. The likely reason your route is not working is that you are confusing the two. Specifically, for it to work as you configured, you would need an MVC controller (that is, a controller that inherits System.Web.Mvc.Controller).
So, assuming you want to go with Web API as your question would indicate, you first need to ensure the correct definition of your controller. It should inherit from System.Web.Http.ApiController.
public class BooksController : ApiController
{
[HttpGet]
public IHttpActionResult BookFilter(string author, string title, string genre, string isbn)
{
return Ok("Successful result");
}
}
Next, you need to put your routing in the WebApiConfig.cs file, not in the RouteConfig.cs file. Don't forget to remove your route from the RouteConfig.cs file.
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "BookFilter",
routeTemplate: "api/books/{author}/{title}/{genre}/{isbn}",
defaults: new { controller = "Books", action = "BookFilter" });
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
}
}
You also need to ensure that the call to GlobalConfiguration.Configure(WebApiConfig.Register); is in your application startup path (by default in Global.asax).
public class MvcApplication : 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);
}
}
Make sure to use the same parameter name in your action (eg. change author to authorId):
Optionally, you can also specify default values for these parameters at your RouteConfig, as follows:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "BookFilter",
url: "api/books/{authorId}/{titleId}/{genreId}/{isbn}",
defaults: new { controller = "Books", action = "BookFilter", authorId= UrlParameter.Optional, titleId = UrlParameter.Optional, genreId = UrlParameter.Optional, isbn = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
Controller:
[HttpGet]
public IQueryable<BookDTO> BookFilter(int authorId, int titleId, int genreId, int isbn)
{
//filter books here
return db.Books.ProjectTo<BookDTO>();
}

WebApi And MVC applications not running together

I am facing problem while running "Asp.net WebApi with EntityFramework Identity" and "MVC SPA" in the single application, either of the one runs at a time, below is my code Global.asax > Application_Start where startup configuration is given.
AreaRegistration.RegisterAllAreas();
GlobalConfiguration.Configure(WebApiConfig.Register);
GlobalConfiguration.Configure(ODataConfig.Register);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Which route registration calls first that only application is accessible, in above sequence only WebApis be accessible, if I change the sequence and call Application route first then only Default web application runs.
Any help would be appreciated.
Let me know if you need additional information regarding this.
thanks in advance!!
Edit:
All the configs are given below.
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 }
);
}
WebApiConfig:
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 }
);
}
ODataConfig:
public static void Register(HttpConfiguration config)
{
ODataModelBuilder builder = new ODataConventionModelBuilder();
builder.EntitySet<Entities.Models.ListaValore>(typeof(Entities.Models.ListaValore).Name);
var model = builder.GetEdmModel();
config.Routes.MapODataRoute("odata", "odata", model);
config.EnableQuerySupport();
}

Url routing with static name mvc

I have a controller name Dashboard and inside that controller i have an action AdminDashboard . Now by default url of this action becomes /Dashboard/AdminDashboard . I want to map this action to this url /SupervisorDashboard
This is what i am doing but its saying not found
routes.MapRoute(
name: "SupervisorDashboard",
url: "SupervisorDashboard",
defaults: new { controller = "Dashboard", action = "AdminDashboard" }
);
and also how can i redirect to this page using Url.Action
Global.asax
public class MvcApplication : System.Web.HttpApplication
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "SupervisorDashboard",
url: "SupervisorDashboard",
defaults: new { controller = "Dashboard", action = "AdminDashboard" }
);
}
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
}
}
Have you placed this new route definition before default route? Routes are evaluated in the same order in which they were registered. If you put default route before any of custom routes, it will be used (and since you probably don't have any SupervisorDashboardController in code, 404 will be returned).
Url.Action should work correctly, if routes are defined in correct order.
So, for this case, RouteConfig should look like this:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
// this one match /SupervisorDashboard only
routes.MapRoute(
name: "SupervisorDashboard",
url: "SupervisorDashboard",
defaults: new { controller = "Dashboard", action = "AdminDashboard" }
);
// should be last, after any custom route definition
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}

ApiController and Controller in a single project route conflict

I have 2 controllers( and ApiController and a Controller). My ApiController calls a provider to get data from the database while my Controller returns a view. I have created separate route configs for them. However, it seems like it is having problems on identifying which route to use? I'm not really sure
RouteConfig.cs
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{resource}.aspx/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { id = UrlParameter.Optional }
);
}
}
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 }
);
config.EnableSystemDiagnosticsTracing();
}
}
Global.asax.cs
public class WebApiApplication
{
protected void ApplicationStart()
{
WebApiAuthConfig.Register(GlobalConfiguration.Configuration);
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
AutoMapperConfig.RegisterMappings();
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
}
}
The routes work fine when accessing within the project. However, when the HttpClient tries to contact the ApiController using the route stated above (Api/../..), it could not contact the controller. It seems like it's confused with the route.
Instead of setting up the standard web routing in the WebApi portion of the code, consider doing it the other way around, setting up WebApi routing in the standard web routing portion of the code. This works in a project I have access to:
public class MvcApplication : HttpApplication {
protected void Application_Start() {
GlobalConfiguration.Configure(WebApiConfig.Register);
RouteConfig.RegisterRoutes(RouteTable.Routes);
...
}
}
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}
);
}
}
public static class WebApiConfig {
public static void Register(HttpConfiguration config) {
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new {id = RouteParameter.Optional}
);
}
}
You might find it better to factor out your API into a separate project. You might then also need to factor out a common data access project that your Web and API projects both depend upon but the resulting rationalized structure will make it easier to resolve config issues of the sort you describe and build failures in one area e.g. API will not stop other projects building.
In WebApiConfig.cs, your routeTemplate should be api/{controller}/{action}/{id}

Resources