ASPNET HttpErrors redirect to controller - asp.net-mvc

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?

Related

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.

mvc 5 error handling not working after third folder

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. :)

Can a "MVC 5 route" be used as a path in an error element in system.webServer httpErrors section in web.config?

Can a "MVC 5 route" be used as a path in an error element in system.webServer httpErrors section in web.config?
<configuration>
...other config stuff...
<system.webServer>
<httpErrors errorMode="Custom">
<remove statusCode="404"/>
<error statusCode="404" path="/Error/NotFound" responseMode="ExecuteURL"/>
</httpErrors>
</system.webServer>
</configuration>
Yes, you can do that. With responseMode="ExecuteURL" IIS internally "redirects" (sends a request for the given URL) and writes the result to the response.
You might also want to set the existingResponse attribute in httpErrors to Replace.
Be aware that:
For architectural reasons, IIS 7.0 can only execute the URL if it is located in the same Application Pool. Use the redirect feature to execute a Custom Error in a different Application Pool.

Custom 400 with message being overwritten

I have some custom error messages I want to display to the user. So when they say enter a bad username/password combo I might do this in my code:
Response.StatusCode = 400;
return Json("Invalid username or password");
This works fine on my local machine. I've been deploying to a web host and these messages are being overwritten by the server so that they all come out as:
The page cannot be displayed because an internal server error has
occurred.
The server is obviously trying to protect me from revealing sensitive information about what went wrong but I actually do want this error to go through and be seen by the user (well really, parsed by my code and then displayed).
I assume there's something in web.config I can do to make this work but everything I've tried thus far hasn't work. Any ideas?
Relevant section of web.config as I have it now:
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="PassThrough">
</httpErrors>
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>
etc.
By the default, some HTTP status codes are obscured by IIS and replaced by it. However, it can be configured by using this line:
<system.webServer>
<httpErrors errorMode="Detailed"/>
This will allow you to pass your custom message in a response and to read it on the client side when AJAX call is done.
In order to handle each Http error code you need to add the specific mapping on the web.config file
<customErrors mode="On/RemoteOnly" >
<error statusCode="400" redirect="My400View" />
<error statusCode="404" redirect="My404iew" />
<error statusCode="500" redirect="My500View" />
</customErrors>
The solution to this ended up being removing the < httpErrors > element from web.config entirely. Even setting it to its default options as seen here http://www.iis.net/configreference/system.webserver/httperrors didn't work so I'm a bit perplexed why removing it works.
<system.webServer>
**Remove <httpErrors> !**
<validation validateIntegratedModeConfiguration="false"/>
<modules runAllManagedModulesForAllRequests="true"/>

Redirect to Home/Index when page not found

This has already been asked here but I was hoping there was a nicer "routing" way to do this.
Essentially I want to redirect to the Home/Index page when a user enters an incorrect url in my site.
EDIT
I'm using IIS.
IMHO the best way will be to use Home/Index as 404 error handling page. So user will be redirected to a home page each time 404 is returned.
<?xml version="1.0"?>
<configuration>
<system.web>
<!-- For IIS6 and Cassini -->
<customErrors mode="RemoteOnly">
<error redirect="Home/Index" statusCode="404"/>
</customErrors>
</system.web>
<system.webServer>
<!-- For IIS7 -->
<httpErrors>
<error statusCode="404" path="Home/Index" />
</httpErrors>
</system.webServer>
</configuration>
Or use IIS7 Rewrite module.
Or you could implement your own Route concrete class accepting every input and repopulating routing dictionary with values: action="Index", controller="Home" and removing everything else from it.
You should add that's implementations instance as last to the routing collection.

Resources