Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC customError page doesn't get displayed for some of the 400 errors

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.

like image 794
Edi Avatar asked May 06 '15 08:05

Edi


1 Answers

There's a lot of misinformation and/or out-of-date solutions regarding error handling in IIS 7+. The main things to understand are:-

  • .NET 4.0 made breaking changes to ASP.NET's request validation.
  • IIS 7+ introduced a new way of handling custom error pages.

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.

like image 108
Iain Galloway Avatar answered Nov 19 '22 18:11

Iain Galloway