Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can web.config httpErrors section and a WebAPI area coexist?

I have an ASP.net MVC 5 project that contains a WebAPI in a specific 'API' area. I have IIS7 error handling enabled in my web.config like this:

<system.webServer>
    <httpErrors errorMode="Custom" existingResponse="Replace">
        <remove statusCode="400" subStatusCode="-1" />
        <remove statusCode="404" subStatusCode="-1" />
        <remove statusCode="500" subStatusCode="-1" />
        <error statusCode="400" path="400.html" responseMode="File" />
        <error statusCode="404" path="404.html" responseMode="File" />
        <error statusCode="500" path="500.html" responseMode="File" />
    </httpErrors>
</system.webServer>

This displays friendly messages to the user of the MVC website when 404/500 etc. occur. My problem arises when specific (legitimate) status codes are returned from the WebAPI (e.g. 400 when `/api/token' is called). In these cases, the JSON content of the reponse is intercepted by IIS and my friendly message HTML is returned as the response instead of the original JSON from the WebAPI. Is it possible to exclude the 'API' area from IIS error handling? If this cannot be done, what is the correct solution for allowing ASP.net MVC website friendly messages and WebAPI JSON responses to coexist?

like image 286
votive Avatar asked May 21 '14 10:05

votive


1 Answers

After much reading and experimentation I found this combination of settings to work effectively:

The Friendly Error Pages

One aspx and one html page for each response status code (here's mine):

  • 404.aspx
  • 404.html
  • 500.aspx
  • 500.html

The only difference between the two pages is that the aspx pages contain the following lines:

<% Response.StatusCode = 500 %>
<% Response.TrySkipIisCustomErrors = true %>

The first line sends the correct HTTP status code back to the client, and the second line attempts to persuade IIS that it doesn't need to handle the response itself.

Web.config Setup

Custom Errors should be on, or remoteonly, and point to the aspx files:

<customErrors mode="On" defaultRedirect="500.aspx" redirectMode="ResponseRewrite">
  <error statusCode="404" redirect="404.aspx" />
</customErrors>

IIS custom errors should be on too, and point to the html files in the system.webServer section:

<httpErrors errorMode="Custom" existingResponse="Auto">
  <remove statusCode="404" subStatusCode="-1" />
  <remove statusCode="500" subStatusCode="-1" />
  <error statusCode="404" path="404.html" responseMode="File" />
  <error statusCode="500" path="500.html" responseMode="File" />
</httpErrors>

The existingResponse="Auto" tells IIS to only return the friendly error pages if the SetStatus flag is set. Effectively this allows ASP.net to send back a custom response, its own custom error page from the customErrors section, or allow IIS to return the configured friendly error page.

FilterConfig.cs Setup

A default ASP.net MVC/WebAPI project is configured with a HandleErrorAttribute filter that handles exceptions raised from actions and returns the correct configured custom error page. I have extended this class to handle exceptions from WebAPI actions by deriving from this class:

filters.Add(new HandleExceptionAttribute());

HandleExceptionAttribute

public class HandleExceptionAttribute : HandleErrorAttribute
{
    public override void OnException(ExceptionContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAjaxRequest() && filterContext.Exception != null)
        {
            filterContext.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            filterContext.HttpContext.Response.StatusDescription = filterContext.Exception.Message;
            filterContext.ExceptionHandled = true;
            filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
        }
        else
        {
            base.OnException(filterContext);
        }
    }
}

This class handles exceptions from WebAPI actions and returns the message of the exception as a JSON response (with the correct HTTP status) to the caller. You might not want to do this if your exception messages aren't user friendly, or if the client doesn't know how to interpret these messages.

like image 162
votive Avatar answered Oct 14 '22 00:10

votive