title: CustomErrors vs HttpErrors - A significant design flaw?
body: <p>As we might know, from e.g. <a href="https://stackoverflow.com/q/2480006/681538">What is the difference between customErrors and httpErrors?</a>, <a href="http://msdn.microsoft.com/en-us/library/h0hfz6fc%28v=vs.85%29.aspx" rel="noreferrer">CustomErrors</a> is a older way to define error pages in a web application but this approach have some issues - for example if you care about correct http response codes since the approach of CustomErrors is to redirect to an error page instead of replacing the current response, which destroys most of the semantic integrity of the communication by http status codes.</p>
  
  <p><a href="http://www.iis.net/configreference/system.webserver/httperrors" rel="noreferrer">HttpErrors</a> is a newer feature available since IIS 7.0 which is running on the server level instead of the application level, and is much better suited to handle error responses in a valid way, for example by using the current response instead of redirecting.</p>
  
  <p>However, if seems to me that the infrastructure for these artifacts in ASP.NET as it looks today gives us some problem.</p>
  
  <p><strong>A simple configuration as example</strong> </p>
  
  <pre><code>&lt;httpErrors existingResponse="Auto" errorMode="Custom"&gt;
      &lt;remove statusCode="404"/&gt;
      &lt;error statusCode="404" path="/Error/E404" responseMode="ExecuteURL" /&gt;
  &lt;/httpErrors&gt;
  </code></pre>
  
  <p>We set <em>errorMode</em> to <em>Custom</em> because we want to test the error handling itself, and we set <em>existingResponse</em> to <em>Auto</em>, which will introduce a branch dependent on <a href="http://msdn.microsoft.com/en-us/library/system.web.httpresponse.tryskipiiscustomerrors%28v=vs.110%29.aspx" rel="noreferrer">Response.TrySkipIisCustomErrors</a>:</p>
  
  <ul>
  <li><strong>True</strong>: the existing erroneous response will pass through this module unhandled, which makes total sense considering it's semantic meaning.</li>
  <li><strong>False</strong>: the existing erroneous response will be replaced by the HttpErrors module if it has a matching rule, which makes just as much sense.</li>
  </ul>
  
  <p>This would <em>ideally</em> allow us to handle some errors ourselves, like when the product in <em>../product/id</em> does not exist, where we can manually can return a specific 404 page with information about missing products and still let the HttpErrors module handle all the rest like <em>../products/namebutshouldbeid</em> or just <em>../misspelledandunmatchableurl</em>.</p>
  
  <p>However, as far as I can see, this is not working. The reason is in the internal method <em><a href="http://msdn.microsoft.com/en-us/library/system.web.httpresponse.aspx" rel="noreferrer">System.Web.HttpResponse</a>.ReportRuntimeError</em>, which will be called on runtime errors (e.g. controllers/actions not found) and where we have a section that looks like this:</p>
  
  <pre><code>// always try to disable IIS custom errors when we send an error
  if (_wr != null) {
      _wr.TrySkipIisCustomErrors = true;
  }
  
  if (!localExecute) {
  code = HttpException.GetHttpCodeForException(e);
  
  // Don't raise event for 404.  See VSWhidbey 124147.
  if (code != 404) {
      WebBaseEvent.RaiseRuntimeError(e, this);
  }
  
  // This cannot use the HttpContext.IsCustomErrorEnabled property, since it must call
  // GetSettings() with the canThrow parameter.
  customErrorsSetting = CustomErrorsSection.GetSettings(_context, canThrow);
  if (customErrorsSetting != null)
      useCustomErrors = customErrorsSetting.CustomErrorsEnabled(Request);
  else
      useCustomErrors = true;
  }
  </code></pre>
  
  <p>While first debugging this I saw that <em>useCustomErrors</em> was set to false, and I could not understand why since I knew that I had a working <em>HttpError</em> configuration, since it works where I return a <a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.httpnotfoundresult%28v=vs.118%29.aspx" rel="noreferrer">HttpNotFoundResult</a> from a controller.</p>
  
  <p>Then I realized that this is not <em>HttpErrors</em>, but the older <em>CustomErrors</em>. And the <em>CustomErrors</em> has obviously no knowledge of <em>HttpErrors</em>.</p>
  
  <p><strong>The "bug"</strong></p>
  
  <p>So what is happening is that <a href="http://msdn.microsoft.com/en-us/library/system.web.httpresponse.tryskipiiscustomerrors%28v=vs.110%29.aspx" rel="noreferrer">Response.TrySkipIisCustomErrors</a> is set to true and since no <em>CustomErrors</em> are defined, it returns the detailed 404 response. At this point we would like <em>HttpErrors</em> to kick in, but it wont because of <em>TrySkipIisCustomErrors</em> now is set to true.</p>
  
  <p>And we can't use <em>CustomErrors</em> either, since this would bring us back to the issues with repugnant error redirections.</p>
  
  <p>And the reason that returning <a href="http://msdn.microsoft.com/en-us/library/system.web.mvc.httpnotfoundresult%28v=vs.118%29.aspx" rel="noreferrer">HttpNotFoundResult</a> works is because it will not trigger a runtime error, only return a 404-result which <em>HttpErrors</em> will intercept as expected, as long as we avoid to set <a href="http://msdn.microsoft.com/en-us/library/system.web.httpresponse.tryskipiiscustomerrors%28v=vs.110%29.aspx" rel="noreferrer">Response.TrySkipIisCustomErrors</a> to true.</p>
  
  <p><strong>How should/could this be handled/resolved?</strong></p>
  
  <p>I'm thinking that <em><a href="http://msdn.microsoft.com/en-us/library/system.web.httpresponse.aspx" rel="noreferrer">System.Web.HttpResponse</a>.ReportRuntimeError</em> should not be allowed to set <a href="http://msdn.microsoft.com/en-us/library/system.web.httpresponse.tryskipiiscustomerrors%28v=vs.110%29.aspx" rel="noreferrer">Response.TrySkipIisCustomErrors</a> to true by default, since we have another errorhandling module dependent on this. Thus, this method needs to be aware of any <a href="http://www.iis.net/configreference/system.webserver/httperrors" rel="noreferrer">HttpErrors</a> configuration as well, either by avoiding setting <em>TrySkipIisCustomErrors</em> to true if we have any <a href="http://msdn.microsoft.com/en-us/library/h0hfz6fc%28v=vs.85%29.aspx" rel="noreferrer">CustomErrors</a> configuration, or that it handles the <em>HttpErrors</em> configuration together with the <em>CustomErrors</em> configuration.</p>
  
  <p>Or have I missed some secret magic to resolve this?</p>