To be correct to our visitors and the search engines, we should return meaningful HTTP status codes if errors occur, facing our site. It's not fair to return HTTP status code 200 on error, even if at the same time we return a view, explaining that an error occurred. if the user types in an incorrect address (the most frequent user fault), we should return HTTP status code 404 and not return or redirect to a View, where status code 200 will be returned.
Here we come to the MVC global error handling basic rules considering custom errors settings in Web.config and global error filters in Global.asax.cs:
1. Have HandleErrorAttribute registered in your Global.asax.cs.
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
filters.Add(new HandleErrorAttribute());
}
In fact, it is there by default, called in Application_Start()
. Don't remove it. Otherwise the framework will display it's fallback message "Server Error in '/' Application...
".
By default above filter captures HTTP errors 500 and displays the /Views/Shared/Error.cshtml view, but you can customize it setting the View property like filters.Add(new HandleErrorAttribute()){View="AnotherView"}
.
2. Have the customErrors settings in Web.config.
<customErrors mode="On" defaultRedirect="~/ErrorGeneric.htm" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="~/Error404.htm" />
<error statusCode="405" redirect="~/Error405.htm" />
</customErrors>
This is required for the filter under (1) to work properly. Besides, this is the way to handle errors outside of controller actions, e.g. incorrect path in the address bar. And, again, to avoid the fallback message "Server error in '/' Application...".
Note that we are having redirectMode="ResponseRewrite"
, not "ResponseRedirect"
. Direct error indication is better than redirecting. Besides, redirecting is prone to unsuitable http status codes returned.
Exception: Depending on MVC version you may get .htm content displayed as flat text by browsers, not rendered as HTML. If so, then just rename above .htm files to .aspx, not .cshtml. (This is a workaround for a bug, don't be so astonished. We need a correct 'content-type' header and this is resolved by using .aspx. Hope this changes in the future.)
Special case: Http error 500 is not captured by Application_Error(), but by the HandleError filter as explained in previous topic. Rather leave it as is, it's for the special case of really unexpected, hard to control application errors. But if you yet definitely want error 500 to be handled as a custom error, then
create a custom error handler like this:public class CustomHandleErrorAttribute : HandleErrorAttribute {
public override void OnException(ExceptionContext filterContext) {
}
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters) {
// ....
filters.Add(new CustomHandleErrorAttribute());
// ....
}
3. Set the HTTP response status code in Global.asax.cs.
protected void Application_Error() {
var exception = Server.GetLastError();
if(exception is HttpException) {
var httpException = (HttpException)exception;
Response.StatusCode = httpException.GetHttpCode();
}
}
Exception to above rules: Where the game is fully under your control, e.g. the user can request an item from a list and enters a wrong identification, you can call a dedicated view from within your code, but setting status code 404 is highly recommended in this case, too.
Fair play is paying off!