mvc 5 error handling not working after third folder - asp.net-mvc

I have an MVC 5 app and with custom errors defined in web.config:
<customErrors mode="RemoteOnly" redirectMode="ResponseRedirect" defaultRedirect="~/Errors/GeneralError">
<error statusCode="404" redirect="~/Errors/NotFound" />
<error statusCode="500" redirect="~/Errors/ServerError" />
</customErrors>
if I type a none existing url it will handle it just fine as long as it is 3 levels/folders maximum. I also have Elmah installed and it will log the error. On the other hand, if I try accessing a none existing url with 4 or more levels/folders, the error will not be handled. It won't redirect to the custom error page, or the default error page. Instead, it will return a blank page with a 404 header. meaning:
website.com/blahblah
website.com/a/blahblah
website.com/a/b/blahblah
will be handled, but website.com/a/b/c/blahblah and above won't. Additionally, I have some pages with 4 or more folders, and they work fine. This problem occurs on local production computer and on the published site on Azure. Anyone has an insight of why?
Solution
Thanks to #Nicholas Patton's link I made these chages:
(1) I removed customErrors completely
(2) added this to web.config:
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404"/>
<error statusCode="404" path="/Errors/NotFound" responseMode="ExecuteURL" />
<remove statusCode="403"/>
<error statusCode="403" path="/Errors/NotFound" responseMode="ExecuteURL" />
<remove statusCode="500"/>
<error statusCode="500" path="/Errors/ServerError" responseMode="ExecuteURL" />
</httpErrors>
(3) In contrary to the article's suggestion using .aspx files I used regular actions in MVC. For example, in controller Error I used NotFound action like so:
[Route("Errors/NotFound")]
public ActionResult NotFound()
{
Response.StatusCode = 404;
return View("Error404");
}
There are a few differences now. the 404 page will not be redirect to Error/NotFound?aspxerrorpath=/errorURL/a/b/c it will simply show the custom error page in the original link without redirection. If you still want the aspxerrorpath value you can simply get the URL in the action like this:
[Route("Errors/NotFound")]
public ActionResult NotFound()
{
string aspxerrorpath = Request.RawUrl;
// do whatever with aspxerrorpath
Response.StatusCode = 404;
return View("Error404");
}
Nonetheless, Elmah still doesn't log 404 errors above 3 folders. But this is a different issue for a new question.

Read this:
https://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging
From the link above:
...what if someone navigates to a URL which isn't handled by ASP.NET?
For example try navigating to http://{your-website}/a/b/c/d/e/f/g. The
route is not mapped to ASP.NET and therefore the Application_Error
event will not be raised.
This is a really great tutorial if you really want your custom error pages to be beefy. :)

Related

configuration custom errors gives "error too many redirects" in mvc

I configured my config file for custom errors in MVC. So the custom error configurations are:
<customErrors mode="On">
<error statusCode="401" redirect="~/Views/UnAuthorized/ErrorUnauthorised"/>
<error statusCode="404" redirect="~/Views/UnAuthorized/ErrorResourceNotFound"/>
</customErrors>
<authentication mode="Windows" />
<authorization>
<deny users="?"></deny>
<allow users="*"></allow>
</authorization>
and for the server
<httpErrors errorMode="Custom" >
<remove statusCode="401" />
<error statusCode="401" path="http://localhost:port/UnAuthorized/ErrorUnauthorised" responseMode="Redirect" />
<remove statusCode="404"/>
<error statusCode="404" path="http://localhost:port/UnAuthorized/ErrorResourceNotFound" responseMode="Redirect" />
</httpErrors>
My Error pages are in different folder with other config file which allows unauthenticated users with:
<allow users="*"></allow>
My Error pages controller is:
public class UnAuthorizedController : Controller
{
// GET: UnAuthorized
public ActionResult ErrorUnauthorised()
{
return View("ErrorUnauthorised");
}
public ActionResult ErrorResourceNotFound()
{
return View("ErrorResourceNotFound");
}
}
When I try to start application it starts with this page ~/Views/UnAuthorized/ErrorUnauthorised and shows browser message "Too many redirects" When I remove httpErrors section the application starts normally with Index page, but on error it doesn't shows the custom error pages
EDIT
I ended up with conclusion that application shows error "too many redirects" if I have both 401 and 404 errors in my config file. If I remove the 401 error the application is working fine
You dont need the httpErrors tag. Just add the following under system.web
<customErrors mode="On" defaultRedirect="errorpage.htm?error=1" >
<error statusCode="404" redirect="filenotfound.htm?error=1" />
<error statusCode="401" redirect="notauthorized.htm?error=1" />
</customErrors>
notes:
1- I add the '?error=1' query string to inform asp.net that i dont want the aspxerrorpath query string to be added when an error occurs.
2- Put your .htm files at the application root folder.
3- If you have some messages you need to generate with c# code and display them inside the error pages then use the application_error event in global.asax to catch the relevant error and redirect the user to desired page.

Can an ASP.NET MVC Area display its own set of error pages?

I've been consolidating some of my web apps into one main ASP.NET MVC project; assigning some of them to separate Areas, so that they can be accessed via subdomains.
Using this (quite helpful) resource (https://dusted.codes/demystifying-aspnet-mvc-5-error-pages-and-error-logging), I've set up customErrors and httpErrors in the Web.config, so that custom error pages are displayed. Works well.
I'll use different layout/styling per Area/subdomain, so I'm wondering:
How can I get an Area to display its own set of error pages?
With the current setup, all the subdomains will display the main set of custom errors that are added to the customErrors and httpErrors sections (403.html, 404.html, et cetera); but I'd prefer tailored error pages for some subdomains. (If one of the areas is exclusively handled by a separate domain altogether, for example, it won't be practical to serve the regular error pages.)
Update:
Here is the scenario, with code, as requested. Thanks to Ben Foster, who offered good guidance here: http://benfoster.io/blog/aspnet-mvc-custom-error-pages2. I've put the code for customErrors, but not the corresponding httpErrors... left it out for brevity.
<system.web>
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/500.aspx">
<error statusCode="404" redirect="~/404.aspx" />
<error statusCode="500" redirect="~/500.aspx" />
</customErrors>
</system.web>
<location path="MyArea1">
<system.web>
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/Areas/MyArea1/500.aspx">
<error statusCode="404" redirect="~/Areas/MyArea1/404.aspx" />
<error statusCode="500" redirect="~/Areas/MyArea1/500.aspx" />
</customErrors>
</system.web>
</location>
<location path="MyArea2">
<system.web>
<customErrors mode="On" redirectMode="ResponseRewrite" defaultRedirect="~/Areas/MyArea2/500.aspx">
<error statusCode="404" redirect="~/Areas/MyArea2/404.aspx" />
<error statusCode="500" redirect="~/Areas/MyArea2/500.aspx" />
</customErrors>
</system.web>
</location>
The above code works well:
If I navigate to "example.com/does/not/exist", I get the
expected error page at ~/404.aspx.
If I navigate to "example.com/MyArea1/does/not/exist", I will
get the custom error page at ~/Areas/MyArea1/404.aspx.
The challenge:
Now I'd like an Area (MyArea2) to be served by a totally separate domain (e.g. exampleOnMyOtherDomain.com), using HostDomainConstraint (as recommended by #TetsuyaYamamoto, in a comment below). A link that would have been accessible via "example.com/MyArea2/validlink" will now be accessed this way: "exampleOnMyOtherDomain.com/validlink".
Now, if I try "exampleOnMyOtherDomain.com/does/not/exist", I'll be served the top-level 404 (~/404.aspx). This is probably because "MyArea2" is not in the path anymore, so the location with the path "MyArea2" will not be picked up.
How can I get the Area (MyArea2) to serve its own error pages?
Based on some research, and helpful pointers from #TetsuyaYamamoto, I've employed the strategy below.
I'm handling the "Application_Error" event in Global.asax, using the following code:
protected void Application_Error(object sender, EventArgs e)
{
string hostName = Request.Headers["host"].Split(':')[0];
if (hostName.Contains("exampleOnMyOtherDomain"))
{
Exception exception = Server.GetLastError();
Response.Clear();
HttpException httpException = exception as HttpException;
Response.TrySkipIisCustomErrors = true;
switch (httpException.GetHttpCode())
{
case 404:
Response.StatusCode = 404;
Server.Transfer("~/Errors/MyArea2_404.htm");
break;
case 500:
default:
Response.StatusCode = 500;
Server.Transfer("~/Errors/MyArea2_500.htm");
break;
}
Server.ClearError();
}
}
Points to note:
hostName.Contains("exampleOnMyOtherDomain") should compare the Host name with the Area I'm interested in handling. I'll add else...if statements for other Areas;
Response.TrySkipIisCustomErrors = true should prevent IIS from attempting to handle the error;
Response.StatusCode sets the status code appropriately ('404', '500'...);
Server.Transfer() reads in a file that I'd like to display as the error page;
Server.ClearError() should signal that the error has already been handled.
I want customErrors/httpErrors to continue handling regular errors. When execution passes through the Application_Error block without being handled (i.e. Server.ClearError() isn't called), customErrors/httpErrors will handle the error.
It seems that this is a decent strategy for handling errors for Areas that are served by a different domain.

Custom error pages in IIS

I have the following set-up in my web.config
<customErrors mode="RemoteOnly" defaultRedirect="/ServerError" redirectMode="ResponseRewrite"/>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="400" subStatusCode="-1" />
<remove statusCode="404" subStatusCode="-1" />
<remove statusCode="500" subStatusCode="-1" />
<error statusCode="400" path="/error/Error400" responseMode="ExecuteURL" />
<error statusCode="404" path="/error/Error404" responseMode="ExecuteURL" />
<error statusCode="500" path="/error/Error500" responseMode="ExecuteURL" />
</httpErrors>
With the above configuration, our website is able to catch 404s, and display the nice 404 page served from /error/error404 with the 404 Header.
Now i have the following scenario:
1. User navigates to /action/id
2. Id is not found, i would like to return a 404, but serve a custom 404, different from the generic one served from /error/error404. i.e. serve a different custom page;
In my controller i have the following:
Response.StatusCode=(int)HttpStatusCode.NotFound;
Response.TrySkipIisCustomErrors = true;
return View("SomeView", model);
Is this something that can be achieved ?
PS: using IIS 8
LE In case anyone is interested in the solution i implemented, i have done the following:
1. In my action, i set a session variable Session["InvalidId"]=something
2. In my ErrorController i have the following:
var invalidId = Session["InvalidId"];
if(invalidId != null)
{
return View("SomeView", model);
}
This helped me serve a different 404 page to my generic one.
Thanks for the help !
Yes, this can be easily achieved and you do not really need other pages. You need to have some session variables for custom messages and use them to determine whether you need to display some messages. If so, then display them in your view.
Algorithm:
Your custom page detects an error
Some error messages are generated
Your custom page redirects to the error page
The error page checks for error messages to be displayed
If there are such error messages, then they are displayed and the error message queue is cleared
Depending on how you handle your exceptions, you could create your own exception class that inherits from HttpException.
[Serializable]
public class MyHttpException : HttpException
{
// Constructors ...
// You could either allow for an http status code to be passed in a
// constructor (to make your class more reusable) or you could call the
// base constructor with 404
// Additional properties if needed ...
}
Then, in you action, when your condition is met (i.e. id not found) you can throw a new MyHttpException.
Your exception handling mechanism can test to see if the current exception is a MyHttpException and react to that with a custom view. It should also set the correct response status code, like you were doing.
The status code can come from MyHttpException since it is a HttpException:
(HttpStatusCode)myHttpException.GetHttpCode()
I'm not sure what controllers you have or how you capture application-wide exceptions but what I have is an ErrorController with an Index action that takes an Exception as a parameter:
public ActionResult Index(Exception exception) { ... }
It is this action that decides what gets returned based on the exception object, similar to what I described above.
Hope this helps.
P.S. I have a similar <httpErrors>...</httpErrors> setup in my web.config and my custom exception works with a 404 status code as described above.

ASPNET HttpErrors redirect to controller

In my web config I have this setting:
<httpErrors errorMode="Custom" defaultPath="/Error/Error/ErrorHandler/404" defaultResponseMode="ExecuteURL" />
I would like to redirect all IIS errors to go to my own controller and action. However I get the error:
The requested page cannot be accessed because the related configuration data for the page is invalid.
However, If I try to redirect specific error codes to my controller, it will work. E.g:
<httpErrors errorMode="Custom" >
<remove statusCode="403"/>
<error statusCode="403" responseMode="ExecuteURL" path="/Error/Error/ErrorHandler/404"/>
</httpErrors>
How can I change my default redirect to go to my controller/action?
Looks like this is not possible. responseMode only supports:
File for unprocessed files, like .html
ExecuteURL supports .aspx files
Redirect to redirect to a different page which is not recommended from a SEO perspective.
You could try the answer from mono blaine in this post: CustomErrors vs HttpErrors - A significant design flaw?

How to set Error page in webconfig for mvc web based system

I have MVC 4 web based system. I need to handle error page in web.config file. I m try do with httpError syntax. When 404 it will show blank page. not show the actual error page. url also not show as error page.
web.config code:
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<error statusCode="404" responseMode="ExecuteURL" path="~/Error/PageNotFound.htm" />
</httpErrors>
please help this.
Try looking at this page: Error Handling and Nice Error pages.
It says to add the following in the Web.Config:
<customErrors mode="On" defaultRedirect="~/Content/Errors/page500.aspx" redirectMode="ResponseRewrite">
<error statusCode="404" redirect="~/Content/Errors/page404.aspx" />
</customErrors>
I haven't got my MVC code infront of me but I think this is what I did.

Resources