2

In short, I've written an error handler controller that has a "HandleError" action that handles all http errors that I care to handle.

For a few reasons (outlined in this answer), I'm catching http errors in two places. One is a global Application_EndRequest method (managed pipeline) and the other is via the <httpErrors> section in the web config (native custom errors module).

If you're wondering why I need the <httpErrors> section, it's because certain responses aren't always caught by the managed pipeline, for example the StaticFile handler catches all urls like ".html,.txt" and will not trigger MVC code.

My controller action looks close to this

public ActionResult HandleError(int statusCode = 0, Exception exception = null)
    {
      string responseProcessed = Request.Headers.Get("__p__");

      if (responseProcessed == null)
      {
        Request.Headers.Add("__p__", "1");

        switch (statusCode)
        {
          case 401:
            return Unauthorized();
          case 403:
            return Forbidden();
          case 404:
            return NotFound();
          case 500:
            return InternalError(exception);
          default:
            return UnhandledError(exception, statusCode);
        }
      }
      else
      {
        return null;
      }
    }

My web.config httpErrors section is currently

<httpErrors errorMode="Custom" existingResponse="Replace">
      <remove statusCode="404"/>
      <error statusCode="404" path="/Errors/HandleError?statusCode=404" responseMode="ExecuteURL"/>
</httpErrors>

Problem

The problem is, the HandleError method is called twice per 404 response because it first hits the custom errors module (specified via the web.config) and then my Application_EndRequest hook catches the 404 which also triggers my action.

Question

The question is, how can I make my HandleError action do nothing to the response instead of replacing it with a blank response (which is what happens when you return null in an action).

Note

Please note that I am aware that I can make my custom 404 error (in web.config) point to my NotFound action explicitly and just return the view each time even though it gets called twice. There is logic (logging, etc) that still needs to only run once and I think it's better design to have it all point to the HandleError method instead to keep repetitive checking logic down and to only runs the action once.

Community
  • 1
  • 1
Slight
  • 1,541
  • 3
  • 19
  • 38
  • Can you show the code in Application_EndRequest? To solve this you probably will have to do som "resetting" with responese and requests. – Magnus Karlsson Jul 08 '15 at 17:22
  • @MagnusKarlsson Follow that link that I posted that describes error handling options in detail. His solution (at the very top of his post) is pretty much what my code is doing (my RrrorsController is different however). Here's the link: http://stackoverflow.com/a/9026907/1404270 – Slight Jul 08 '15 at 18:54

2 Answers2

0

The simple answer is... you cant. All action methods return a type of ActionResult, no matter what. If you do not need an actionresult (or the default EmptyResult that gets sent when you return null) then don't use an action method and consider using a private method instead.

If you do this, you could do as you suggest with a custom 404 and then call this private HandleError method as needed from within your controller.

If this logic needs ran for every request then consider just put it in an action filter instead.

Brad C
  • 2,868
  • 22
  • 33
  • Yeah, I actually neglected a pretty obvious answer which is similar to what you're saying. I can just check to see if the response was already processed inside the `Application_EndRequest` handler and simply not call the controller action if so. I was genuinely curious though whether you could simply return a result that would not replace/alter the existing response and you've given me the answer is seems. – Slight Jul 08 '15 at 16:37
  • Just tested and turns out I can't use `Application_EndRequest` because the custom error handler runs second which means it ends up processing the request again. Looks like an action filter might be the way to go. – Slight Jul 08 '15 at 16:45
  • Action filter doesn't seem like it would work either since there is no legitimate way to "cancel" a request without overwriting the existing response. – Slight Jul 08 '15 at 17:17
0

I think you can but you might be over complicating stuff. Lets look at that first :) Does this solve your error handling of static files?

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
</system.webServer>

You can read more about it here: http://www.hanselman.com/blog/BackToBasicsDynamicImageGenerationASPNETControllersRoutingIHttpHandlersAndRunAllManagedModulesForAllRequests.aspx

Magnus Karlsson
  • 3,549
  • 3
  • 31
  • 57
  • That would make things easy, but there are performance implications of enabling RAMMFAR and so using it seems off the table. That's why I resorted to the custom error handler which is a native handler that will invoke the managed pipeline only for error statuses. – Slight Jul 08 '15 at 17:12