Friday, February 24, 2012

Custom Errors in WCF REST Services - Part I

Support for REST in WCF has improved enough to make it a viable option for those wanting to expose a RESTful API to their services. Error reporting in such an API is something that should be thought through from the beginning. How will users be notified when something doesn't work right? Getting a consistent data contract for error messages that originate from your application isn't all that hard with a little custom behaviors. However, getting the same data contracts respected by IIS and WCF itself can be a bit of a trick. If you don't take care of all 3 (application, WCF, and IIS) layers, then your REST API response would be difficult to parse. I'm sure your API consumers wouldn't appreciate this:

HTTP/1.1 405 Method Not Allowed
Allow: GET, PUT, DELETE, POST
Content-Type: text/html
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Wed, 11 Jan 2012 02:48:21 GMT
Content-Length: 1293

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/>
<title>405 - HTTP verb used to access this page is not allowed.</title>
<style type="text/css">
<!--
body{margin:0;font-size:.7em;font-family:Verdana, Arial, Helvetica, sans-serif;background:#EEEEEE;}
fieldset{padding:0 15px 10px 15px;} 
h1{font-size:2.4em;margin:0;color:#FFF;}
h2{font-size:1.7em;margin:0;color:#CC0000;} 
h3{font-size:1.2em;margin:10px 0 0 0;color:#000000;} 
#header{width:96%;margin:0 0 0 0;padding:6px 2% 6px 2%;font-family:"trebuchet MS", Verdana, sans-serif;color:#FFF;
background-color:#555555;}
#content{margin:0 0 0 2%;position:relative;}
.content-container{background:#FFF;width:96%;margin-top:8px;padding:10px;position:relative;}
-->
</style>
</head>
<body>
<div id="header"><h1>Server Error</h1></div>
<div id="content">
 <div class="content-container"><fieldset>
  <h2>405 - HTTP verb used to access this page is not allowed.</h2>
  <h3>The page you are looking for cannot be displayed because an invalid method (HTTP verb) was used to attempt access.</h3>
 </fieldset></div>
</div>
</body>
</html>

Part I - Application Generated Errors
Part II - WCF Generated Errors
Part III - IIS Generated Errors

Application Generated Errors
Making sure all of your services conform to the same error data contract is not difficult. WCF comes out of the box with the IErrorHandler extension interface which allows you the opportunity to handle and translate any faults that occur on a service. IErrorHandler has two methods to implement and mapping any faults to a custom error data contract can be done here.

One approach that I've seen work well is to have all faults in your application inherit from a base Fault that has useful properties for your API consumers. You can then use the different fault classes to convey any exception that your application might throw.
[DataContract]
public class FaultBase
{
    public FaultBase(string message, string code)
    {
        this.Message = message;
        this.Code = code;
    }

    public string Message { get; set; }
    public string Code { get; set; }
}

[DataContract]
public class ItemNotFoundFault : FaultBase
{
    public ItemNotFoundFault()
        : base("The resource item was not found.", "ItemNotFound")
    { }
}

Any exception caught by your IErrorHandler implementation can be translated into one of your faults and sent back to the caller. Nothing special here. I'm not going into a lot of detail on this because there are plenty examples of this on the web.

In Part II, I'll discuss how to handle errors generated inside WCF that don't get handled by the IErrorHandler implementation.

Custom Errors in WCF REST Services - Part II

In Part I, I discussed how to handle errors from your WCF services so that you can give your API users a consistent error result.

In Part II, I'll be discussing how to handle a few of those errors generated by WCF that don't get sent to your IErrorHandler implementation.

WCF Generated Errors
Handling the application errors will get you most of the way there. However, there are two cases where WCF will display that custom blue and white HTTP web page similar to what was shown above; 405 Method Not Allowed, and 404 Endpoint Not Found. These cases arise when WCF can't match the request with a service or a method. Unfortunately these errors don't go through the IErrorHandler interface, so you have to do something else to replace the output with your error data contract. I was pretty stumped on this one and had to resort to using Reflector, breakpoints, and stack trace examination to figure out what to do. There's a property on the DispatchRuntime called UnhandledDispatchOperation that WCF uses to dispatch operations that don't match an operation in your service. It's the IOperationInvoker instance stored in this property that is responsible for the blue and white HTML versions of the 405 and 404 responses.

The solution is to replace this instance with your own implementation that doesn't return HTML. The tricky part in writing the implementation is trying to determine whether to return a 405 or a 404. I'm guessing that it's possible to use the WCF API to figure out at runtime which case applies to the request. Rather than figure this all out, I cheated and just copied what the original IOperationInvoker instance did.
public class UnhandledOperationInvoker : IOperationInvoker
    {
        public object[] AllocateInputs()
        {
            return new object[1];
        }

        public object Invoke(object instance, object[] inputs, out object[] outputs)
        {
            outputs = null;
            bool uriMatch = false;
            Message message = inputs[0] as Message;

            if (message.Properties.ContainsKey("UriMatched"))
            {
                uriMatch = (bool)message.Properties["UriMatched"];
            }

            if (!uriMatch)
            {
                return new ItemNotFoundMessage();
            }
            else
            {
                return new MethodNotAllowedMessage();
            }
        }

        public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
        {
            throw new NotImplementedException();
        }

        public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
        {
            throw new NotImplementedException();
        }

        public bool IsSynchronous
        {
            get { return true; }
        }
    }


Apparently something in the communication stack figures this out for us and saves this information in the current message's properties. Once you know which case applies to you, just return your standard error response appropriately. In my example, I'm returning an instance of one of my custom classes that inherits from System.ServiceModel.Channels.Message. This way I can control exactly the way the response looks. WARNING! Use the above approach at your own risk. Obviously it will stop working if a new version of .NET starts doing it differently.

In Part III, I'll discuss how to handle errors generated from IIS that don't get handled by WCF at all.

Custom Errors in WCF REST Services - Part III

In Part I, I discussed how to handle errors from your WCF services so that you can give your API users a consistent error result.

In Part II, I discussed how to handle a few of those errors generated by WCF that don't get sent to your IErrorHandler implementation.

Now that we've gotten WCF taken care of, there are even still some situations where an end user can get a bunch of HTML instead of a well formed XML response.

IIS Generated Errors
Now that we've gotten past WCF, there's one more layer that needs to be address. IIS has configuration settings that allow you to control the response that's given to a user for certain HTTP status codes. This might work for you, but if you need finer or programtic control, then you'll need to handle these errors yourself. A good example is the 500 Internal Server Error message. Let's say something in ASP.NET is misconfigured. In this scenario, IIS would give the end user an HTML 500 error. The approach I'm going to show for getting around this is for IIS 7.0. The idea is to write a custom IHttpHandler and tell IIS to route any IIS error to that handler. The handler can then return your custom error data contract to the caller. When IIS routes the request to your handler, it passes the error status code as a query string parameter. So all your handler has to do is parse this out and return the appropriate response.
public class ErrorHttpHandler : IHttpHandler
    {
        public bool IsReusable
        {
            get { return true; }
        }

        public void ProcessRequest(HttpContext context)
        {
            if (context.Request.QueryString.Count == 0)
                return;

            string strStatusCode = context.Request.QueryString[0].Split(';').FirstOrDefault() ?? "500";
            int statusCode = 500;
            int.TryParse(strStatusCode, out statusCode);

            string message = "Unhandled server error.";

            switch (statusCode)
            {
                case 400:
                    message = "Bad request.";
                    break;

                case 404:
                    message = "Item not found.";
                    break;
            }

            context.Response.StatusCode = statusCode;
            context.Response.Write(string.Format("<Error><Message>{0}</Message></Error>", message));
        }
    }

The above implementation is only to get you started. The last step is to configure IIS to forward errors to your handler.

<system.webServer>
    <httpErrors errorMode="Custom">
      <clear/> 
      <error statusCode="404" path="/application/ErrorHandler" responseMode="ExecuteURL"/>
      <error statusCode="400" path="/application/ErrorHandler" responseMode="ExecuteURL"/>
    </httpErrors>
    <handlers>
      <add name="ErrorHandler" path="ErrorHandler" verb="*" type="Your Application.ErrorHttpHandler, FrameworkAssembly"/>
    </handlers>
  </system.webServer>

The worst part of this approach is that you have to explicitly define which HTTP response codes you want to handle in your handler. It is possible to have a catch all, but doing so requires you to override the system configuration for IIS and I didn't have much success getting that to work on my machine.