I am attempting to "catch all" 500 and 404 errors within my MVC application and I can't seem to grasp what is needed, even after reading all the articles and questions out there.
Web.config (this allows 500 errors to go to ~/Views/Shared/Error.cshtml):
<system.web>
<customErrors mode="On" redirectMode="ResponseRewrite" />
</system.web>
I've setup the HomeController to throw an error to test the above setup:
public ActionResult Index()
{
//Testing errors
throw new Exception("Exception");
return View();
}
In my Global.asax.cs, I have the following to log the 500 errors:
protected void Application_Error()
{
var ex = Server.GetLastError();
//Custom ExceptionLog
new ExceptionLogHelper().Add("Application_Error", Response.Status, ex);
}
Now for the 404 errors:
In my RouteConfig.cs, I have the following routes, but can't seem to catch all 404s:
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Error",
url: "Error/{code}",
defaults: new { controller = "Error", action = "Index", code = UrlParameter.Optional }
);
//routes.MapRoute(
// name: "Controllers",
// url: "{controller}/{action}/{id}",
// defaults: new { controller = "Error", action = "Index", code = UrlParameter.Optional }
//);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
//Keep at bottom
routes.MapRoute("CatchAll", "{*url}", new { controller = "Error", action = "Index", name = "no-route-found", code = "404" });
}
}
CatchAll at the bottom is doing a good job catching everything that does not match the preceeding routes.
I have a bunch more test scenarios, but one that is bugging me is the following UrlParameter:
http://localhost:64275/does/not/exist/
The above URL is essentially http://localhost:64275/{controller}/{action}/{id}
I don't have a Controller named does, and I thought that defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } would default to the Home controller with Action of Index if no Controller was matched.
Another example that works:
http://localhost:64275/a/a/a/a/ (because it has 4 parts, not 3 or less)
Can someone explain where I might be going wrong? ...and what I am not understanding?
Should I be implementing something like this: .Net MVC Routing Catchall not working (Darin Dimitrov's answer)
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));
}
}
You are correct in that the default route is being hit in the
http://localhost:64275/does/not/exist/case. But, routing doesn't have any expensive Reflection calls built in to ensure the controller exists before attempting to create it.However, you can intervene by making your own custom
IControllerFactorythat knows what to do when MVC can't locate the controller instance that is specified in the route.NotFoundControllerFactory.cs
DefaultControllerFactoryaccepts the controller as a string and then attempts to resolve the controller name to a type. If it can't the type isnullwhen it callsGetControllerInstance(RequestContext, Type). So, all we have to do is check whether the type isnulland then rewrite our request so it instantiates theErrorControllerand calls theNotFoundaction method.Usage
You just need to register the controller factory with MVC at startup.
Then to tie everything together, your
NotFoundaction method should set the status code to 404.As for action methods, MVC does respond with a 404 Not Found if the action does not exist on a controller.