I'm having quite an interesting issue with the custom error pages management for a new ASP.NET MVC application.
This issue is like this:
- if I'm calling an URL (doesn't matter which) with a "bad" argument at the end of URL, like ..../c<
, the application is displaying the correct server error page as instructed in the web.config;
- if I'm changing the URL to a more nasty one, like .../<c
(to look more like an HTML tag, there is no more server error page displayed in the browser and instead of that, I'm getting a plain YSOD with a message like An exception occurred while processing your request. Additionally, another exception occurred while executing the custom error page for the first exception. The request has been terminated.
According to ELMAH, both requests ended with a 400 status code and the message being:
- for the first one: System.Web.HttpException (0x80004005): A potentially dangerous Request.Path value was detected from the client (<).
at System.Web.HttpRequest.ValidateInputIfRequiredByConfig()
at System.Web.HttpApplication.PipelineStepManager.ValidateHelper(HttpContext context)
- for the second one: System.Web.HttpException (0x80004005): A potentially dangerous Request.Path value was detected from the client (<).
at System.Web.HttpRequest.ValidateInputIfRequiredByConfig()
at System.Web.HttpApplication.PipelineStepManager.ValidateHelper(HttpContext context)
So, both errors are the same, the status code is the same, but for one of the errors, the custom error page is not getting displayed. I've also went to global.asax in debug mode and checked the Server.GetLastError()
in protected void Application_Error(object sender, EventArgs e)
and again, both errors were the same, nothing is different.
In web.config, this is how my <customErrors>
tag looks like:
<customErrors mode="On" defaultRedirect="/ServerError.aspx" redirectMode="ResponseRewrite">
<error statusCode="500" redirect="/ServerError.aspx" />
<error statusCode="404" redirect="/PageNotFound.aspx" />
</customErrors>
Can please someone tell me why the behavior is different in these two cases?
Thank you very much for your time.
There's a lot of misinformation and/or out-of-date solutions regarding error handling in IIS 7+. The main things to understand are:-
Most people are using hodge-podge solutions involving all of customErrors
, httpErrors
the Application_Error
handler, and often setting requestValidationMode="2.0"
on the httpRuntime
property and/or disabling request validation entirely! This makes it really difficult to use other people's solutions because any and all of these can affect the behaviour. I had a quick search around and I found several semi-duplicates without accepted answers, probably for this reason.
The reason that these two errors give you different behaviour is that they occur at different stages in the request pipeline. The customErrors
node in your web.config
interacts with errors "inside" your application, while request validation takes place "outside" your application. IIS rejects the dangerous request before it gets to your application code, and so your customErrors
replacement doesn't happen.
So how do we fix that?
Ideally you want a solution with as few moving parts as possible. IIS7 gives us a new way to specify error page replacement at the IIS level instead of at the application level - the httpErrors
node. This lets us catch all our errors in one place:-
<configuration>
...
<system.webServer>
...
<httpErrors errorMode="Custom" existingResponse="Replace">
<clear />
<error statusCode="400" responseMode="ExecuteURL" path="/ServerError.aspx"/>
<error statusCode="403" responseMode="ExecuteURL" path="/ServerError.aspx" />
<error statusCode="404" responseMode="ExecuteURL" path="/PageNotFound.aspx" />
<error statusCode="500" responseMode="ExecuteURL" path="/ServerError.aspx" />
</httpErrors>
...
</system.webServer>
...
</configuration>
If you care about SEO (and you should!), you still have to make sure that your controller/page sets an appropriate status code:-
this.Response.StatusCode = 500; // etc.
You should remove your customErrors
node entirely. It is normally used for backwards-compatibility. You should also ensure that requestValidationMode
is not set on the httpRuntime
node.
This should catch most errors (excluding, obviously, errors in parsing your web.config!)
Related:- ASP.NET MVC Custom Errors
MSDN Documentation:- http://www.iis.net/configreference/system.webserver/httperrors
Note: in your case, if you want to set defaultPath
on the httpErrors
node, you'll get a lock violation because of ApplicationHost.config
settings. You can either do as I did and just set path
individually for the error codes you care about, or you can have a look at unlocking the node:-
http://www.iis.net/learn/get-started/planning-your-iis-architecture/introduction-to-applicationhostconfig
http://forums.iis.net/t/1159721.aspx
My intuition is that it's more trouble than it's worth in low-control environments like Azure App Service / Azure Web Sites. You might as well set the paths for individual status codes.
I've put a full working example of using httpErrors for custom error pages up on github. You can also see it live on azure web sites.
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