Effective Error Handling with WCF & REST
I recently received a question regarding effective error handling when working with RESTful web services. In this post, I will illustrate how to expose, as well as handle errors effectively when working with RESTful WFC services.
Watch the Screencast
The Sample Service
As you can see from the ServiceHost directive, we are using the configurationless model. By setting the factory to WebServiceHostFactory, we do not need to define all of the configuration settings in the web.config under System.serviceModel. The WebServiceHostFactory will dynamically set up an instance of the WebServiceHost in response to requests. The WebServiceHost will set up a default endpoint, if appropriate, with the binding set to webHttpBinding (read REST-Friendly binding). Further, this host will add the WebHttpBehavior to the endpoints. The WebServiceHostFactory is our RESTful factory and first shipped with WCF 3.5 (with Visual Studio 2008 sp1).
Examining the service implementation, it is clear that this is a relatively simple service operation that we are exposing. The operation returns a page of wines from our wine catalog (by page, I am referring to returning a subset of wines (1 to x out of y) instead of the complete set). We decorated the operation with a WebGet, exposing the service over an HTTP GET. We further set the ResponseFormat to JSON and set up a friendly UriTemplate. If the above concepts are foreign to you, or you want a refresher on the basics of REST in WCF, check out all of my blog posts on the subject. The last thing to notice is that if someone passes an invalid pageSize, we are throwing an ApplicationException. This is what you might expect to see in a typical .NET implementation. The next step is to cover the basics of errors in RESTful services.
REST and Errors
As you probably know, the underlying theme of REST is to embrace the key protocols of the web. The means in which a successful or failed call is conveyed to the requestor in HTTP is via an HTTP status code. We are all familiar with at least a few of these: 200: OK, 404: Not Found: 400: Bad Request and 500: Internal Server Error. What you may not know is that the codes are logically grouped as follows:
- 100-199 – Informational
- 200-299 – Client request successful
- 300-399 – Client request redirected, further action necessary
- 400-499 – Client request incomplete
- 500-599 – Server error
The appropriate status code is returned to the requestor as an HTTP header, allowing the requestor to determine the result of the call. The response can optionally contain additional information in the response body. We will see some examples of this in this post.
The WCF REST Starter Kit
The WCF REST Starter Kit is a toolkit that provides Visual Studio templates, samples and a dll (Microsoft.ServiceModel.Web.dll) that provides some great functionality. In this post, we will examine some of the types made available in Microsoft.ServiceModel.Web.dll, including the WebServiceHost2Factory, the WebServiceHost2 and a new exception type named the WebProtocolException. To take advantage of the factory and new ServiceHost, you simply need to set a reference to the assembly in your web and set the Factory in the svc file to the new factory (see below). This factory will return instances of the WebServiceHost2 in response to requests. As we will see later in this post, the WebServiceHost2 provides some cool new functionality (as well as changes some default behaviors). Click here to download the starter kit.
Throwing Exceptions in .NET
Well, the first thing I want to look at is what happens if I do nothing. In other words, what is the behavior if I just let .NET exceptions propagate up or I just throw a .NET exception like an ApplicationException. Taking a look at the code above, you can see that is exactly what I am doing. So, let’s take a look at the resulting HTTP status code using both of our ServiceHosts (WebServiceHost via the WebServiceHostFactory and WebServiceHost2 via the WebServiceHost2Factory). The results should be the same, right?
All tests are using the same request url: http://robagby1:46000/Web/WineService.svc/wines?index=0&pageSize=-1
Test 1: Throwing an ApplictionException with the WebServiceHost
Test 2: Throwing an ApplictionException with the WebServiceHost2
(code is same as in Test 1)
As you can see, the two tests returned with differing HTTP Status Codes. The first returned a 400, which is probably the correct code to return. It is telling the requestor that they issued a “Bad Request” for some reason or another. Unfortunately, the error detail would provide little help to the caller what the issue really was. The second test returned a 500, telling the caller that there was an “Internal Server Error”. This would leave the caller thinking that the issue was not with their request, rather with the code on the server. That would be bad. However, the error detail would have been helpful. As you can see, it is a JSON formatted body. The JSON represents an object with one property named “Detail” and the value of the property is “pageSize must be > 0”.
This simple test illustrates that you never want to rely on defaults. They can and do change. What we learned here is that we want to be explicit on what HTTP status code to return. We also (may) want to include some appropriate error details. Let’s take a look at some other, more explicit, solutions.
Setting the Status Code On The Response
On solution is to get a reference to the response and set the HTTP Status Code yourself. That solution works with the original WebServiceHost that shipped with WCF 3.5. There is a helper object named WebOperationContext that provides access to the properties of the Request and Response. In this case, we grabbed a reference to the Response. If the pageSize was less than or equal to 0, we explicitly set the status code of the response to 400, or “Bad Request”. We, however did not send any response. Let’s take a look at the code:
As you can see, using this methodology, we are able to force the appropriate HTTP Status Code. However, we still do not have the capability to easily send additional error details. Let’s move on to another solution, taking advantage of the WebServiceHost2.
Throwing a WebProtocolException with WebServiceHost2
There is a way to get the best of all worlds: the ability to control the HTTP Status Code, as well as to pass additional error details. Interestingly enough, passing the error details is more complex than you might originally think. You need to be aware of the format of the response. If the ResponseFormat is set to Json, the details should be serialized as Json. If it is Xml, it should be serialized as Xml, perhaps XHtml, so it is pleasant to see in the browser. The WCF REST Starter Kit handles all of this for us. All we need to do is to set the Factory to the WebServiceHost2Factory (or implement our own factory) that will return instances of WebServiceHost2. Then we just need to throw our new Exception type: the WebProtocolException. This new type takes the Status Code, as well as the details as an argument. The Status Code will be set on the response and the detail will be serialized according to the RespinseFormat that was set on the operation. Let’s take a look:
Handling service errors from Silverlight uncovers a unique set of problems. Because Silverlight is a plugin, the browser will hide the actual status code and error detail from the client. Take a look at Eugene Osotevsky’s blog post on the subject.
Your RESTful services should return the appropriate HTTP Status Code in the response. You may also want to pass back some additional error detail. The WCF REST Starter Kit provides all of these capabilities with great ease.