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?
After much reading and experimentation I found this combination of settings to work effectively:
One aspx and one html page for each response status code (here's mine):
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.
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.
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());
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.
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