ASP.Net MVC HandleError throwing 500 Internal Server Error - asp.net-mvc

I have the following HandleError filter on my controller:
[HandleError(ExceptionType = typeof(ArgumentException), View = "DestinationError")]
I've set-up the Web.Config so that customErrors are on. The problem I'm having is that the HandleError filter is working fine, when I run the app locally out of Visual Studio, but when I deploy it to the server all I get is a 500 Internal Server Error, indicating the Error view cannot be found.
Has anyone come across this before, I'm suspicious that routing may be the root cause of the problem (hoho). The site gets deployed into a directory in the web root, rather than into the wwwroot itself, so perhaps IIS cannot locate the error file.

To answer my own question the magic is to turn off HTTP Errors in IIS. I'm not delighted in this workaround, so if anyone has any better ideas, I'd love to hear them.

Otherwise you can use the Web.Config configuration and set it to the expected controller's actions.
Like this:
<customErrors mode="On" defaultRedirect="/Error">
<error statusCode="404" redirect="/Error/NotFound"/>
</customErrors>
Then imagine you have an Error controlller (/Error) which points out to an index action
public class ErrorController : Controller
{
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Index()
{
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return View("Index");
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult NotFound()
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View("NotFound");
}
}

What if you try the following?
Response.TrySkipIisCustomErrors = true;

I had the same problem after migrating to MVC 3 RC. Managed to get around it by adding the layout / master page.
#inherits System.Web.Mvc.WebViewPage<System.Web.Mvc.HandleErrorInfo>
#{
View.Title = "Error";
Layout = "~/Views/Shared/_Layout.cshtml";
}
Now the internal server error is gone, but I think it's a bug somewhere.

Related

ASP.NET MVC4 CustomErrors DefaultRedirect Ignored

I have an MVC 4 app, using a custom HandleErrorAttribute to handle only custom exceptions. I would like to intercept the default 404 and other non-500 error pages and replace them with something more attractive. To that end, I added the following to my Web.config:
<system.web>
<customErrors mode="On" defaultRedirect="~/Error/Index" />
...
</ system.web>
I have an Error controller with an Index method and corresponding view, but still I get the default 404 error page. I have also tried setting my defaultRedirect to a static html file to no avail. I have tried adding error handling specific to 404's inside <customErrors>, and I even tried modifying the routes programattically, all with no results. What am I missing? Why is ASP ignoring my default error handling?
Note: I noticed earlier that I cannot test my CustomHandleErrorAttribute locally, even with <customErrors mode="On". It does work when I hit it on my server from my dev box though... not sure if that is related. This guy had the same problem.
This should work :
1. Web.Config
<customErrors mode="On"
defaultRedirect="~/Views/Shared/Error.cshtml">
<error statusCode="403"
redirect="~/Views/Shared/UnauthorizedAccess.cshtml" />
<error statusCode="404"
redirect="~/Views/Shared/FileNotFound.cshtml" />
</customErrors>
2. Registered HandleErrorAttribute as a global action filter in the FilterConfig class as follows
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomHandleErrorAttribute());
filters.Add(new AuthorizeAttribute());
}
If that dont work then, Try to make yourself transfer the response by checking status codes like the Following in the Global.asax: at least it must work.
void Application_EndRequest(object sender, EventArgs e)
{
if (Response.StatusCode == 401)
{
Response.ClearContent();
Server.Transfer("~/Views/Shared/UnauthorizedAccess.cshtml");
}
}
I am going little off topic. I thought this is bit important to explain.
If you pay attention to the above highlighted part. I have specified the order of the Action Filter. This basically describes the order of execution of Action Filter. This is a situation when you have multiple Action Filters implemented over Controller/Action Method
This picture just indicates that let's say you have two Action Filters. OnActionExecution will start to execute on Priority and OnActionExecuted will start from bottom to Top. That means in case of OnActionExecuted Action Filter having highest order will execute first and in case of OnActionExecuting Action Filter having lowest order will execute first. Example below.
public class Filter1 : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Execution will start here - 1
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Execution will move here - 5
base.OnActionExecuted(filterContext);
}
}
public class Filter2 : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
//Execution will move here - 2
base.OnActionExecuting(filterContext);
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
//Execution will move here - 4
base.OnActionExecuted(filterContext);
}
}
[HandleError]
public class HomeController : Controller
{
[Filter1(Order = 1)]
[Filter2(Order = 2)]
public ActionResult Index()
{
//Execution will move here - 3
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
}
You may already aware that there are different types of filters within MVC framework. They are listed below.
Authorization filters
Action filters
Response/Result filters
Exception filters
Within each filter, you can specify the Order property. This basically describes the order of execution of the Action Filters.
Back to the original Query
This works for me. This is very easy and no need to consider any change in Web.Config or Register the Action Filter in Global.asax file.
ok. So, First I am creating a simple Action Filter. This will handle Ajax and Non Ajax requests.
public class MyCustomErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
filterContext.ExceptionHandled = true;
var debugModeMsg = filterContext.HttpContext.IsDebuggingEnabled
? filterContext.Exception.Message +
"\n" +
filterContext.Exception.StackTrace
: "Your error message";
//This is the case when you need to handle Ajax requests
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = new JsonResult
{
JsonRequestBehavior = JsonRequestBehavior.AllowGet,
Data = new
{
error = true,
message = debugModeMsg
}
};
}
//This is the case when you handle Non Ajax request
else
{
var routeData = new RouteData();
routeData.Values["controller"] = "Error";
routeData.Values["action"] = "Error";
routeData.DataTokens["area"] = "app";
routeData.Values["exception"] = debugModeMsg;
IController errorsController = new ErrorController();
var exception = HttpContext.Current.Server.GetLastError();
var httpException = exception as HttpException;
if (httpException != null)
{
Response.StatusCode = httpException.GetHttpCode();
switch (System.Web.HttpContext.Current.Response.StatusCode)
{
case 404:
routeData.Values["action"] = "Http404";
break;
}
}
var rc = new RequestContext
(
new HttpContextWrapper(HttpContext.Current),
routeData
);
errorsController.Execute(rc);
}
base.OnException(filterContext);
}
}
Now you can implement this Action Filter on Controller as well as on the Action only.Example:
Hope this should help you.
I want to share my knowledge after investigating this problem. Any comments that help improve my statements are welcomed.
In ASP.NET MVC, there are three layers that handle HTTP requests in the following order (response is transferred in reverse order):
IIS (HTTP Layer)
ASP.NET (Server Layer)
Controller (MVC Layer)
All of these layers have error handling, but each layer does it differently. I'll start with IIS.
IIS Layer
The simplest example of how IIS handles an error is to request a non existing .html file from your server, using the browser. The address should look like:
http://localhost:50123/this_does_not_exist.html
Notice the title of the browser tab, for example: IIS 10.0 Detailed Error - 404.0 - Not Found.
ASP.NET Layer
When IIS receives a HTTP request, if the URL ends with .aspx, it forwards it to ASP.NET since it is registered to handle this extension. The simplest example of how ASP.NET handles an error is to request a non existing .aspx file from your server, using the browser. The address should look like:
http://localhost:50123/this_does_not_exist.aspx
Notice the Version Information displayed at the bottom of the page, indicating the version of ASP.NET.
The customErrors tag was originally created for ASP.NET. It has effect only when the response is created by ASP.NET internal code. This means that it does not affect responses created from application code. In addition, if the response returned by ASP.NET has no content and has an error status code (4xx or 5xx), then IIS will replace the response according to the status code. I'll provide some examples.
If the Page_Load method contains Response.StatusCode = 404, then the content is displayed normally. If additional code Response.SuppressContent = true is added, then IIS intervenes and handles 404 error in the same way as when requesting "this_does_not_exist.html". An ASP.NET response with no content and status code 2xx is not affected.
When ASP.NET is unable to complete the request using application code, it will handle it using internal code. See the following examples.
If an URL cannot be resolved, ASP.NET generates a response by itself. By default, it creates a 404 response with HTML body containing details about the problem. The customErrors can be used to create a 302 (Redirect) response instead. However, accessing a valid URL that returns 404 response from application code does not trigger the redirect specified by customErrors.
The same happens when ASP.NET catches an exception from application code. By default, it creates a 500 response with HTML body containing details about the source code that caused the exception. Again, the customErrors can be used to generate a 302 (Redirect) response instead. However, creating a 500 response from application code does not trigger the redirect specified by customErrors.
The defaultRedirect and error tags are pretty straight-forth to understand considering what I just said. The error tag is used to specify a redirect for a specific status code. If there is no corresponding error tag, then the defaultRedirect will be used. The redirect URL can point to anything that the server can handle, including controller action.
MVC Layer
With ASP.NET MVC things get more complicated. Firstly, there may be two "Web.config" files, one in the root and one in the Views folder. I want to note that the default "Web.config" from Views does two things of interest to this thread:
It disables handling URLs to .cshtml files (webpages:Enabled set to false)
It prevents direct access to any content inside the Views folder (BlockViewHandler)
In the case of ASP.NET MVC, the HandleErrorAttribute may be added to GlobalFilters, which also takes into account the value of mode attribute of the customErrors tag from the root "Web.config". More specifically, when the setting is On, it enables error handling at MVC Layer for uncaught exceptions in controller/action code. Rather than forwarding them to ASP.NET, it renders Views/Shared/Error.cshtml by default. This can be changed by setting the View property of HandleErrorAttribute.
Error handling at MVC Layer starts after the controller/action is resolved, based on the Request URL. For example, a request that doesn't fulfill the action's parameters is handled at MVC Layer. However, if a POST request has no matching controller/action that can handle POST, then the error is handled at ASP.NET Layer.
I have used ASP.NET MVC 5 for testing. There seems to be no difference between IIS and IIS Express regarding error handling.
Answer
The only reason I could think of why customErrors is not considered is because they are created with HttpStatusCodeResponse from application code. In this case, the response is not altered by ASP.NET or IIS. At this point configuring an alternative page is pointless. Here is an example code that reproduces this behavior:
public ActionResult Unhandled404Error()
{
return new HttpStatusCodeResult(HttpStatusCode.NotFound);
}
In such scenario, I recommend implementing an ActionFilterAttribute that will override OnResultExecuted and do something like the following:
int statusCode = filterContext.HttpContext.Response.StatusCode;
if(statusCode >= 400)
{
filterContext.HttpContext.Response.Clear();
filterContext.HttpContext.Response.Redirect("/Home/Index");
}
The implemented ActionFilterAttribute should be added to GlobalFilters.
Create a Controller ErrorController.
public class ErrorController : Controller
{
//
// GET: /Error/
public ActionResult Index()
{
return View();
}
}
Create the Index view for the action.
in Web.config
<customErrors mode="On">
<error statusCode="404" redirect="Error/Index"/>
</customErrors>
When you are handling errors in your code/logic
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
ViewBag.Message = "Modify this template to jump-start application.";
return View("Index2");
}
}
[HandleError] attribute - will redirected to the Error.cshtml page inside shared folder.
I am not sure this answer will help you but this a simple way... I placed error.html in / and turned mode to on for custom errors in web config and this works perfectly...
<system.web>
<customErrors defaultRedirect="~/Error.html" mode="On" />
</system.web>
this error.html is a basic html page with head and body..
To me, it works deleting the default Error.cshtml file, now it is taking the custom Error defaultRedirect page in Web.config.

ASP.NET MVC 4 - Exception Handling Not Working

When an error occurs in my ASP.NET MVC 4 application, I would like to customize a view for the user depending on the type of error. For example, page not found or an exception has occurred (with some user-friendly details about the exception). I've checked other examples of how to do this on StackOverflow and other online sources, but none of the answers are working for me.
The basic [HandleError] attribute does not seem to be working in VS2012 with an MVC 4 application targeting .NET 4.5. Here is the code I have in my home controller:
[HandleError]
public ActionResult Index()
{
Response.TrySkipIisCustomErrors = true; //doesn't work with or without this
throw new NullReferenceException("Uh oh, something broke.");
}
It is just throwing an exception, and I would expect the default ~/Shared/Error.cshtml view to be returned because of the [HandleError] attribute, but all I get is an HTTP 500 Internal Server Error indicating that the page could not be displayed. I checked my web.config, and different configurations seem to be behaving weird. In the section, it currently contains:
<customErrors mode="On" />
(I've tried adding defaultRedirect and with customErrors mode="Off" as well but that didn't have any effect... neither the shared Error view or the CustomError view I have is being rendered. If I change customErrors mode to off, then I can see the exception details as expected, so it is throwing the "Uh oh, something broke" exception properly.
I've also tried adding an OnException handler to the HomeController, and although I can debug through and see that the OnException event is being raised, it doesn't make any difference:
protected override void OnException(ExceptionContext filterContext)
{
base.OnException(filterContext);
filterContext.ExceptionHandled = true;
if (filterContext == null)
{
filterContext.Result = View("CustomError");
return;
}
Exception e = filterContext.Exception;
// TODO: Log the exception here
ViewData["Exception"] = e; // pass the exception to the view
filterContext.Result = View("CustomError");
}
I have also tried changing [HandleError] to specify a view, but that doesn't seem to do anything either:
[HandleError(View="CustomError")]
Any help would be much appreciated. Please let me know if you need any more details.
I also went on a seamingly endless journey of reading SO answers and assorted blog postings trying to get custom error pages to work. Below is what finally worked for me.
The first step is to use IIS Express for debugging instead of the built-in Cassini web server to "guarantee" that the debug experience will mirror the live environment.
Create a controller to handle application errors and add an action for each custom error you will handle. The action should do any logging, set the status code, and return the view.
public class ErrorsController : Controller
{
// 404
[HttpGet]
public ActionResult NotFound()
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View();
}
// I also have test actions so that I can verify it's working in production.
[HttpGet]
public ActionResult Throw404()
{
throw new HttpException((int)HttpStatusCode.NotFound, "demo");
}
}
Configure the customErrors section in web.config to redirect to your custom error actions.
<system.web>
<customErrors mode="RemoteOnly" defaultRedirect="Errors/InternalServerError">
<error statusCode="400" redirect="Errors/BadRequest" />
<error statusCode="403" redirect="Errors/Forbidden" />
<error statusCode="404" redirect="Errors/NotFound" />
<error statusCode="500" redirect="Errors/InternalServerError" />
</customErrors>
Add the httpErrors section to system.webServer and set the errorMode to Detailed in web.config. Why this works is a mystery to me, but this was the key.
<system.webServer>
<httpErrors errorMode="Detailed" />
Add a catchall route last to the defined routes to direct 404s to the custom page.
// catchall for 404s
routes.MapRoute(
"Error",
"{*url}",
new {controller = "Errors", action = "NotFound"});
You may also want to create a custom HandleErrorAttribute and register it as a global filter to log 500 errors.
These steps worked for me in both development (IIS Express) and production (IIS7) environments. You need to change customErrors mode="On" to see the effect in development.
I seem to recall that you had to call the page from a non-localhost IP address (typically another computer). And it has to be an IIS based server, not the built-in development server (so IIS or IIS Express, but then you have to configure IIS Express for external access, which is a pain).
You can in fact debug it, you have to configure your local server on your debug computer to accept external requests, then call your local server from the remote server.
I faced a similar problem and lost sometime trying to work out what was going on. So just in case any others face a similar problem here is what my problem was.
The error page was trying to use my _Layout page. Just ensure that you Error.cshtml page has
#{
Layout = null;
}
Try adding the following attribute to the customErrors tag:
defaultRedirect="Error"
This explicitly defines to which view MVC should redirect the user when the error is thrown and no view is specified by default in the attribute. This will of course only work if Error.cshtml exists in the Shared views folder.

How to implement proper HTTP error handling in .NET MVC 2?

I've been struggling all day to implement error handling in my ASP.NET MVC 2 app. I've looked at a variety of techniques, but none work properly. I'm using MVC2 and .NET 4.0 (started the project before MVC3 was released; we'll upgrade after we deliver our initial release).
At this point, I'll be happy to properly handle 404 and 500 errors -- 403 (authorization required) would be great, too, followed by various other specific responses. Right now, I either get all 404s, all 500s, all 302s before the 404, or all 302s before the 500.
Here are my requirements (which should be pretty close to the basic requirements of HTTP):
If a resource is not found, throw a 404, and display a 404-specific page with the requested URL. DO NOT return an intermediate response code like 302. Ideally, keep the requested URL, rather than showing a new URL like /Error/NotFound -- but if the latter displays, be sure we didn't return a redirect response to get it.
If an internal server error occurred, throw a 500, and display a 500-specific error with some indication of what went wrong. Again, don't return an intermediate response code, and ideally don't change the URL.
Here's what I'd consider a 404:
Static file not found: /Content/non-existent-dir/non-existent-file.txt
Controller not found: /non-existent-controller/Foo/666
Controller found, but Action not found: /Home/non-existent-action/666
Controller and action found, but the action can't find the requested object: /Home/Login/non-existent-id
Here's what I'd consider a 500:
Post a bad value: POST /User/New/new-user-name-too-long-for-db-column-constraint
Non-data-related problem, like a Web Service endpoint not responding
Some of these problems need to be identified by specific controllers or models, and then the controllers should throw the appropriate HttpException. The rest should be handled more generically.
For 404 case #2, I tried to use a custom ControllerFactory to throw a 404 if the controller can't be found.
For 404 case #3, I've tried to use a custom base controller to override HandleUnknownAction and throw a 404.
In both cases, I get a 302 before the 404. And, I never get 500 errors; if I modify Web.config to put a typo in my Web Service endpoint, I still get a 302, then a 404 saying the URL (controller/action) which uses the Web Service can't be found.
I also get the requested URL as a(n unwanted) querystring param: /Error/NotFound?aspxerrorpath=/Home/non-existent-action
Both of these techniques came from http://www.niksmit.com/wp/?p=17 (How to get normal 404 (Page not found) error pages using ASP.Net MVC), pointed to from http://richarddingwall.name/2008/08/17/strategies-for-resource-based-404-errors-in-aspnet-mvc/
If in Web.config I have <customErrors mode="On" defaultRedirect="~/Error/Unknown" redirectMode="ResponseRedirect" />, I get the appropriate response code, but my Error controller never gets called. Taking out the redirectMode attribute gets me the MVC error views, but with an intervening 302 and a changed URL -- and always the same controller (Unknown = 500; if I change it to NotFound everything looks like a 404).
Here are some of the other things I've read and tried to implement:
http://www.davidjuth.com/asp-net-mvc-error-handler.aspx
http://sanjayuttam.com/wordpress/index.php/c-sharp/c-sharp-code-examples/error-handling-in-asp-net-mvc-1-part-2-of-2/
http://blog.hebbink.com/post/2010/12/14/NET-custom-404-error-page-returns-302-for-http-status.aspx
http://blog.dantup.com/2009/04/aspnet-mvc-handleerror-attribute-custom.html
http://weblogs.asp.net/scottgu/archive/2008/07/14/asp-net-mvc-preview-4-release-part-1.aspx
.. along with a bunch of StackOverflow posts.
Seems to me this sort of error handling is pretty basic to Web apps, and the MVC framework ought to have defaults that do this out of the box, and let people extend it to work otherwise. Perhaps they'll do it in a future release. In the meantime, can someone give me comprehensive details on how to implement proper HTTP responses?
Here's one technique you could use. Define an ErrorsController which will serve the error pages:
public class ErrorsController : Controller
{
public ActionResult Http404()
{
Response.StatusCode = 404;
return Content("404", "text/plain");
}
public ActionResult Http500()
{
Response.StatusCode = 500;
return Content("500", "text/plain");
}
public ActionResult Http403()
{
Response.StatusCode = 403;
return Content("403", "text/plain");
}
}
and then in Global.asax you could subscribe for the Application_Error event where you could log the exception and execute the corresponding action of the ErrorsController:
protected void Application_Error(object sender, EventArgs e)
{
var app = (MvcApplication)sender;
var context = app.Context;
var ex = app.Server.GetLastError();
context.Response.Clear();
context.ClearError();
var httpException = ex as HttpException;
var routeData = new RouteData();
routeData.Values["controller"] = "errors";
routeData.Values["exception"] = ex;
routeData.Values["action"] = "http500";
if (httpException != null)
{
switch (httpException.GetHttpCode())
{
case 404:
routeData.Values["action"] = "http404";
break;
case 403:
routeData.Values["action"] = "http403";
break;
case 500:
routeData.Values["action"] = "http500";
break;
}
}
IController controller = new ErrorsController();
controller.Execute(new RequestContext(new HttpContextWrapper(context), routeData));
}
And now all that's left is to start throwing proper exceptions:
public class HomeController : Controller
{
public ActionResult Index()
{
throw new HttpException(404, "NotFound");
}
}
For HTTP 404 errors (without redirects) take a look at my blog post on the subject. This might give you some good ideas:
http://hectorcorrea.com/blog/returning-http-404-in-asp-net-mvc/16
This doesn't answer your question, but it is important to note that HTTP status 500 indicates that something went wrong on the server, so your example:
POST /User/New/new-user-name-too-long-for-db-column-constraint
Is not valid grounds to throw a 500, its a data validation issue and should be handled by MVC data annotations or a jQuery validation framework or etc. Just showing an error message next to the TextBox saying "User Name too long" is much better.
This is a very old question. but I thought It's worth it if I introduce you to a much much cleaner way to handle Http Exceptions that I saw in dear "Jesse Webb's answer".
The solution is to use the httpErrors element of the system.webServer section:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" subStatusCode="-1" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL" />
<error statusCode="500" path="/Error" responseMode="ExecuteURL" />
</httpErrors>
You also can log all exceptions in this way. "Read the "Jesse Webb's answer"".
This really feels much cleaner and also works as well as every other solution (without redirect).
Note: This only works work in IIS 7 and and newer. (Because of the httpErrors element which was recently added.

Can't find the custom error page

Here's how I have it defined (locally, on my development machine):
<customErrors mode="On" defaultRedirect="Error.aspx">
<error statusCode="404" redirect="NotFound.aspx" />
</customErrors>
And I have the [HandleError] attribute:
[Authorize]
[HandleError]
public class HomeController : Controller
{ // etc.
Yet when I type in http://localhost:1986/blah, I get the following error:
The resource cannot be found.
Description: HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly.
Requested URL: /NotFound.aspx
The URL it's trying to go to is as you would expect:
http://localhost:1986/NotFound.aspx?aspxerrorpath=/blah
So it IS attempting to go to the custom error file -- however it can't find it. I do have NotFound.aspx in the Shared directory -- same place as the Error.aspx supplied by Microsoft as a default. Why can't it find it?
If the Error.aspx and NotFound.aspx are in the shared directory is there a controller wired to served them? If you do not have some sort of controller route configured to serve the files then the fact that they are in the shared folder is irrelevant.
You have a few options, you could create an ErrorController which will handle the requests for those views and define routes pointing to those controller actions:
[OutputCache(CacheProfile = "Default", VaryByParam = "none")]
public class ErrorController : DefaultAreaBaseController
{
public ViewResult ServiceUnavailable() {
Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
return View("ServiceUnavailable");
}
public ViewResult ServerError() {
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return View("ServerError");
}
public new ViewResult NotFound() {
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View("NotFound");
}
}
Or as an alternative, you can create ignore routes pointing at the physical files and place the error pages somewhere other than the Views folder (like your root directory):
routes.IgnoreRoute("Error.aspx/{*pathInfo}");
routes.IgnoreRoute("NotFound.aspx/{*pathInfo}");
Either of these solutions is viable however depending on your configuration using an IgnoreRoute() may be more ideal as it will forgo the need to pipe the request to MVC only to serve a static error page.
Option One:
is to build an Errors Controller with a "NotFound" view along with a "Unknown" view. This will take anything that is a 500 Server error or a 404 NotFound error and redirect you to the appropriate URL. I don't totally love this solution as the visitor is always redirected to an error page.
http://example.com/Error/Unknown
<customErrors mode="On" defaultRedirect="Error/Unknown">
<error statusCode="404" redirect="Error/NotFound" />
<error statusCode="500" redirect="Error/Unknown" />
</customErrors>
wwwroot/
Controllers
Error.cs
Views/
Error/
NotFound.aspx
Unknown.aspx
Option Two:
I Definitely don't prefer this method (as it is basically reverting back to web forms, The second option is to simply have a static Error.aspx page and ignore the route in MVC), but it works none the less. What you're doing here is ignoring a "Static" directory, placing your physical Error pages in there, and skirting around MVC.
routes.IgnoreRoute("/Static/{*pathInfo}"); //This will ignore everything in the "Static" directory
wwwroot/
Controllers/
Static/
Error.aspx
Views/
Option Three:
The third option (THIS IS MY FAVORITE) is to return an Error View from whatever view is catching the error. This would require you to code up Try/Catch blocks along the way for "known" errors and then you can use HandleError for the unknown errors that might creep up. What this will do is preserve the originally requested URL but return the ERROR view.
EXAMPLE:
http://example.com/Products/1234 will show a details page for ProductID 1234
http://example.com/Products/9999 will show a NotFound error page because ProductID 9999 doesn't exist
http://example.com/Errors/NotFound "should" never be shown because you handle those errors individually in your controllers.
Web.Config
<customErrors mode="On">
</customErrors>
Controller
// Use as many or as few of these as you need
[HandleError(ExceptionType = typeof(SqlException), View = "SqlError")]
[HandleError(ExceptionType = typeof(NullReferenceException), View = "NullError")]
[HandleError(ExceptionType = typeof(SecurityException), View = "SecurityError")]
[HandleError(ExceptionType = typeof(ResourceNotFoundException), View = "NotFound")]
Public Class ProductController: Controller{
public ViewResult Item(string itemID)
{
try
{
Item item = ItemRepository.GetItem(itemID);
return View(item);
}
catch()
{
return View("NotFound");
}
}
}
Folder Structure
wwwroot/
Controllers/
Shared/
NotFound.aspx
NullError.aspx
SecurityError.aspx
SqlError.aspx
Views/
Option Four:
The last option would be that you build your own custom filter for things like ResourceNotFoundException and attach it to your controller class. This will do the exact same thing as above but with the added benefit of sending the error code down the line to the client as well.
Richard Dingwall talks about it on his blog.
Your mixing web forms and MVC concepts here. Turn custom errors off in the web.config. Then in the HandleError attribute optionally specify the type and view, by default error.aspx is searched for in views\CurrentController then views\shared. Whilst you can get 404 handling working with the HandleError filter you probably want to create a filter just for 404 handling, the reasons and how to are explained in detail here:
http://richarddingwall.name/2008/08/17/strategies-for-resource-based-404-errors-in-aspnet-mvc/

How to use the [HandleError] attribute in ASP.NET MVC?

I've got some custom error handling in my ASP.NET MVC site -> seems to be working fine.
I have an Error Controller, like this :-
/Controller/ErrorController.cs
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult Unknown()
{
Response.StatusCode = (int)HttpStatusCode.InternalServerError;
return View("Error");
}
[AcceptVerbs(HttpVerbs.Get)]
public ActionResult NotFound()
{
Response.StatusCode = (int)HttpStatusCode.NotFound;
return View("Error");
}
(i've also removed some other text, like the message to display, etc..)
The view is located like..
/Views/Error/Error.aspx
Now, i'm not sure how I can 'call' the Unknown method (above) when an error occurs. I thought I could use the [HandleError] attribute, but this only allows me to specify a View (and I'm don't think i'm doing that properly, cause the error url is really weird).
The [HandleError] attribute allows you to specify error pages for unhandled exceptions in your controller classes as opposed to the default custom error handling provided by ASP.NET.
I'd rather use traditional ASP.NET custom error handling unless in some controllers you want to ovveride default custom error behaviour.
<customErrors mode="On" defaultRedirect="~/Error/Unknown">
<error statusCode="404" redirect="~/Error/PageNotFound"></error>
<error statusCode="500" redirect="~/Error/Server"></error>
<error statusCode="503" redirect="~/Error/Server"></error>
</customErrors>

Resources