I've spent the day going through a dozen or more sites, blogs, and SO answers, all claiming to provide the "right" way to implement custom error handling, in ASP.NET MVC.
None of them agree with each other, and most of them don't seem to work.
What I want:
And, I want this to be the canonical answer. Not just "this is what I do", but "this is what Microsoft's developers intended and expected to be done".
Any ideas?
I'm not sure if this is the best way but it works for me and its seo friendly: web.config
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" responseMode="ExecuteURL" path="/Error/NotFound" />
</httpErrors>
</system.webServer>
you can define sub status codes too:
<error statusCode="404" subStatusCode="2" responseMode="ExecuteURL" path="/Error/NotFound" />
and this is how my error controller looks like:
public class ErrorController : Controller
{
//
// GET: /Error/
public ActionResult NotFound()
{
// you have to set your error codes manually
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View();
}
}
The following solution meets most of your requirements except:
Create an ErrorController - this allows you to tailor your end-user error pages and status codes.:
[AllowAnonymous]
public class ErrorController : Controller
{
public ActionResult PageNotFound()
{
Response.StatusCode = 404;
return View();
}
public ActionResult ServerError()
{
Response.StatusCode = 500;
return View();
}
public ActionResult UnauthorisedRequest()
{
Response.StatusCode = 403;
return View();
}
//Any other errors you want to specifically handle here.
public ActionResult CatchAllUrls()
{
//throwing an exception here pushes the error through the Application_Error method for centralised handling/logging
throw new HttpException(404, "The requested url " + Request.Url.ToString() + " was not found");
}
}
Add a route to catch all urls to the end of your route config - this captures all 404's that are not already caught by matching existing routes:
routes.MapRoute("CatchAllUrls", "{*url}", new { controller = "Error", action = "CatchAllUrls" });
In your global.asax:
protected void Application_Error(object sender, EventArgs e)
{
Exception exception = Server.GetLastError();
//Error logging omitted
HttpException httpException = exception as HttpException;
RouteData routeData = new RouteData();
IController errorController = new Controllers.ErrorController();
routeData.Values.Add("controller", "Error");
routeData.Values.Add("area", "");
routeData.Values.Add("ex", exception);
if (httpException != null)
{
//this is a basic exampe of how you can choose to handle your errors based on http status codes.
switch (httpException.GetHttpCode())
{
case 404:
Response.Clear();
// page not found
routeData.Values.Add("action", "PageNotFound");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
break;
case 500:
// server error
routeData.Values.Add("action", "ServerError");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
break;
case 403:
// server error
routeData.Values.Add("action", "UnauthorisedRequest");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
break;
//add cases for other http errors you want to handle, otherwise HTTP500 will be returned as the default.
default:
// server error
routeData.Values.Add("action", "ServerError");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
break;
}
}
//All other exceptions should result in a 500 error as they are issues with unhandled exceptions in the code
else
{
routeData.Values.Add("action", "ServerError");
Server.ClearError();
// Call the controller with the route
errorController.Execute(new RequestContext(new HttpContextWrapper(Context), routeData));
}
}
In Summary:
There are 2 types of error handling.
For page-level errors you can use both web.config or you can create base controller and override OnException event.
protected override void OnException(ExceptionContext filterContext) { ... }
For application-level errors you can use global.asax Application_Error event
void Application_Error(object sender, EventArgs e) { ... }
Also you can check this link: https://www.simple-talk.com/dotnet/asp.net/handling-errors-effectively-in-asp.net-mvc/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With