I can't seem to figure this out. I'm experimenting with MVC Beta and am trying to implement a catchall route such that if the user enters mysite.com/blah instead of mysite.com/home/index it will hit the "Error" route.
Unfortunately it seems that the "Default" route always catches "blah" first. In fact the only route I've been able to get to the "Error" route with is blah/blah/blah/blah.
Is this the way it's supposed to work, because I've seen other examples that have the "Default" and "Error" route set up just like this and it seems that if they were to type in a controller that doesn't exist it would hit the "Error" route.
Is there something I'm missing (very possible) or will I just have to create a specific route for each controller?
Code I'm using:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
routes.MapRoute(
"Error",
"{*catchall}",
new { controller = "Base", action = "Error", id = "404" }
);
Thank you,
Jeff
Your first route will catch the most urls since you have defaults for the elements, you can visualize this using the route debugger from Phil Haack, see the link:
Route Debugger
In order to handle errors I used the Application_Error event in one of my projects:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
HttpException httpException = exception as HttpException;
if (httpException != null)
{
RouteData routeData = new RouteData();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("action", "HttpError500");
if (httpException.GetHttpCode() == 404)
{
routeData.Values["action"] = "HttpError404";
}
Server.ClearError();
Response.Clear();
IController errorController = new ErrorController();
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
}
MVC routes are checked in the order that they are entered.
Mysite/blah will be found by the default route. The controller will be blah, and the action is index.
When you entered the mysite/blah/blah/blah/blah route you gave it a route it could not map the default route to and then your catchall route was called.
For those other examples, did you notice if they had some error filters setup? I'm pretty sure the default asp.net mvc site has some error handling attributes on the pages already.
This can also help when dealing with MVC catchall problems:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{*id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" } // Parameter defaults
);
That is, where it says {id}, change it to {*id}. This allows the final id parameter to consume as much additional path as might be passed in. The default rule accepts this:
/person/name/joe
But not this:
/products/list/sortby/name
The second URL will throw a 404 without this modification to the route.
Related
I am having a hard time trying to redirect to my error handling controller when an application error is encountered in Global.asax.
Here is my RouteCollection
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"DefaultError",
"{controller}/{action}/{id}",
new { controller = "HndlError", action = "AllErrors", id = UrlParameter.Optional }
);
And My Global.asax Application_Error has this
this.Context.Response.Clear();
this.Context.ClearError();
//this.Response.Redirect("~/HndlError/AllErrors") // This one Works !!!
this.Response.RedirectToRoute( new { controller = "HndlError",
action = "AllErrors", id = ErrorMessage }); // This does not Work
this.Response.End();
My Controller Action does get hit while using Response.Redirect but returns a blank page with RedirectToRoute . Did some more searching and then came across this GEM !
Beware of ResponseRedirectToRoute in MVC 3
Does that mean it will not work in MVC3 or am i missing something ? Please Help.
thanks
Try the following:
protected void Application_Error(object sender, EventArgs e)
{
HttpContext ctx = HttpContext.Current;
ctx.Response.Clear();
RequestContext rc =((MvcHandler)ctx.CurrentHandler).RequestContext;
rc.RouteData.Values["action"] = "AllErrors";
rc.RouteData.Values["controller"] = "HndlError";
rc.RouteData.Values["id"] = ErrorMessage ;
IControllerFactory factory = ControllerBuilder.Current.GetControllerFactory();
IController controller = factory.CreateController(rc, "HndlError");
controller.Execute(rc);
ctx.Server.ClearError();
}
When attempting to create a catch all route in MVC 4 (something I've found several examples of, and based my code on) it returns a 404 error. I'm running this on IIS 7.5. This seems like a straight forward solution, so what am I missing?
One note, if I move the "CatchAll" route above the "Default" route it works. But of course then none of the other controllers are ever reached.
Here is the code:
Route.Config:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
"CatchAll",
"{*dynamicRoute}",
new { controller = "CatchAll", action = "ChoosePage" }
);
Controller:
public class CatchAllController : Controller
{
public ActionResult ChoosePage(string dynamicRoute)
{
ViewBag.Path = dynamicRoute;
return View();
}
}
Since the ultimate goal of creating the catchall route was to be able to handle dynamic urls and I was unable to find a direct answer to the original issue above, I approached my research from a different perspective. In doing so I came across this blog post: Custom 404 when no route matches
This solution allows handling of multiple sections within a given url
(i.e. www.mysite.com/this/is/a/dynamic/route)
Here is the final custom controller code:
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (String.IsNullOrEmpty(controllerName))
{
throw new ArgumentException("MissingControllerName");
}
var controllerType = GetControllerType(requestContext, controllerName);
// This is where a 404 is normally returned
// Replaced with route to catchall controller
if (controllerType == null)
{
// Build the dynamic route variable with all segments
var dynamicRoute = string.Join("/", requestContext.RouteData.Values.Values);
// Route to the Catchall controller
controllerName = "CatchAll";
controllerType = GetControllerType(requestContext, controllerName);
requestContext.RouteData.Values["Controller"] = controllerName;
requestContext.RouteData.Values["action"] = "ChoosePage";
requestContext.RouteData.Values["dynamicRoute"] = dynamicRoute;
}
IController controller = GetControllerInstance(requestContext, controllerType);
return controller;
}
It's probably because whatever route your're testing this with is matching your 1st - Default route. The way the routing in MVC works, any address you pass in will try to match routes in your routes collection in order of appearance. Once it find the 1st matching route it aborts further execution. In this case your Default route is 1st one in the list so if it is matched your second route will never be examined.
Basically write something like http://www.mysite.com/Home/Testing/Item/Page in your address bar and this should fail to match to your Default route and then try to match the CatchAll route.
Try defining the optional string dynamicRoute parameter on your route:
routes.MapRoute(
"CatchAll",
"{*dynamicRoute}",
new { controller = "CatchAll", action = "ChoosePage", dynamicRoute = UrlParameter.Optional } );
I'm on some page in my MVC3 application. The URL in the browser is http://localhost:60901/MyApp/Ideas/Create/. Now I get an exception on this page after posting some data. I have error handling. Simplified version is shown below.
Global.asax
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
RouteData routeData = new RouteData();
routeData.Values.Add("action", "General");
routeData.Values.Add("e", exception);
using (ErrorController errorController = new ErrorController())
{
((IController)errorController).Execute(new RequestContext(new HttpContextWrapper(HttpContext.Current), routeData));
}
}
ErrorController.cs
public class ErrorController : Controller
{
public ActionResult General(Exception e)
{
ViewBag["ReturnToUrl"] = Url.Action("Index", "Ideas");
return View();
}
}
The ReturnToUrl in ViewBag suprisingly is //Ideas/Index/
What with the MyApp prefix ? Normally, Url.Action("Index", "Ideas") returns /MyApp/Ideas/Index/. Is there something wrong with the controller context ? How to fix this ?
Why not just set the ViewBag["ReturnToUrl"] = "/MyApp/Ideas/Index/" manually.
Jarek,
Is "MyApp" an Area of your application? If so, you can specify the area as part of the route values as so:
Url.Action("Action", "Controller", new { Area = "MyApp" });
If "MyApp" is not an area, maybe you can explain your configuration a bit more.
Hopet this helps,
-covo
I've found the problem. It was my mistake. I missed the case, that all routes in the system I'm currently working with, are defined as below:
routes.MapRoute(
"Default", // Route name
"{application}/{controller}/{action}/{*id}", // URL with parameters
new { controller = "Welcome", action = "Index", id = UrlParameter.Optional, application = UrlParameter.Optional } // Parameter defaults
);
They have an {application} parameter at the beginning (before the {controller} parameter).
Previously, I didn't pass this parameter to the ErrorController. After passing it
routeData.Values.Add("application", "MyApp");
program works as expected.
I want to reserve the root of my website to be for standard webforms and have the MVC pages in a subdirectory Views so I have the following..
routes.MapRoute(
"Default", // Route name
"Views/{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
However, even though this works and I see a few work-arounds, I'm not quite happy that the RedirectToAction seems to direct me to the wrong page e.g.
return RedirectToAction("Index", "Home");
Takes me to http://localhost/Views which gives me a resource not found and the Index action on the HomeController doesn't fire. Is there a better way of implementing what I want here or am I missing something obvious?
As you know Views is kind of a reserved name in ASP.NET MVC. It's an existing directory. You could set the RouteExistingFiles to true in your route definitions:
public static void RegisterRoutes(RouteCollection routes)
{
routes.RouteExistingFiles = true;
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default",
"views/{controller}/{action}/{id}",
new { controller = "home", action = "index", id = UrlParameter.Optional }
);
}
Now when you navigate to http://example.com/views or http://example.com/views/homeor http://example.com/views/home/index it will be the Index action of Home controller that will get executed.
In my application, I have some controllers that use specific routes and some that use the default route. As a result, I have something like this:
//.. other more specific routes
routes.MapRoute(
"Workout - Specific Workouts by exercise",
"{userName}/workouts/exercise/{exerciseID}/{exerciseName}",
new { controller = "Workout", action = "WorkoutsByExercise" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);
routes.MapRoute(
"Error",
"{*catchall}",
new { controller = "Error", action = "Http404" }
);
However, if I type something like ~/Home/SomethingThatDoesntExist it will be picked up by the default router and thus never gets to my catchall route handler. I've verified this through Phil Haack's route debugger. As a result, it will try to get to that controller/action and wont find it. In return, StructureMap throws an error which bubbles up to Application_Error where I can't redirect it (despite everything I've tried and through research).
So, is there anyway around this or do I have to specify specific routes for all of my controllers and actions AND how can I redirect to a specific page/controller once I'm in Application_Error()?