System.ServiceModel.Web and FaultExceptions

For those who may not already know WCF that well, FaultException is a special type of exception class that exists as a way for those of us who are writing service operations that need to throw errors to indicate to those who are extending the WCF stack that “hey, I want the details of this exception to be passed through to the client”. The whole purpose of FaultException is so that if something like a database statement fails unexpectedly deep inside the implementation of your operation, the details of that exception, which may include sensitive SQL syntax or schema object names for example, will not make their way back to the client. Instead it will be caught and replaced with generic details saying that something bad happened on the server side.

Out of the box the various implementations of channels, behaviors, etc. that come with WCF understand the semantics of FaultException and process it correctly using the logic explained above. In fact, the ServiceDebugBehavior even includes a property called IncludeExceptionDetailInFaults to disable the swallowing of non-FaultException details so that they are returned to the client. This is great for debugging and QA environments, but obviously it’s a setting you want turned off when going to production.

Unfortunately it seems that System.ServiceModel.Web’s support for FaultExceptions is lacking. Assuming a production environment where IncludeExceptionDetailInFaults is turned off, for any webHttpBinding endpoint, if you throw a FaultException from your operation, that exception is returned to the client with an HTTP status of “400 Bad Request” and the body of the response will be an HTML page that tells you to check the “server log for details”. The Reason of the FaultException will not be included anywhere in the the output. The encoding used doesn’t matter, the result is the same for both POX and JSON. In fact, the only time that the reason text is properly returned from a WebHttpBinding is when you use the EnableWebScriptBehavior which is what adds support for the specific semantics of JSON requests for ASP.NET AJAX clients. This is actually a “good thing” considering that ASP.NET AJAX 1.0’s ScriptHandlerFactory that adds support for JSON calls to ASMX based web services completely swallowed all exception messages unless you turned customErrors=”Off” for your whole web application which is of course a really bad idea for the same reason you wouldn’t want includeExceptionDetailInFaults=”true” in a production environment.

Next, let’s talk about FaultException<T> and fault details. FaultException<T> is a special subclass of FaultException that you can raise to include details, in the form of a data contract, that you want returned to the client beyond the standard Reason, Code, etc. of a FaultException. This is basically the feature that allows you to return structured information about your fault rather than shoving everything into the reason text and expecting your clients to parse that string somehow. System.ServiceModel.Web does not appear to support this concept right now either AFAICT. So if you were expecting to see these details returned in the body of the response rather than some string explanation, you’re also SOL.

Finally, let’s talk about the HTTP statuses that are returned when exceptions are raised out of operations exposed via the WebHttpBinding. Currently any exception thrown will result in a “400 Bad Request”. I believe is the perfect status to return when there is a FaultException thrown because a FaultException definitely means there was something wrong with the logical operation. However, it I don’t think it’s the right status to return if a non-FaultException is raised. Instead I think a “500 Internal Server Error” should be thrown in that case. The difference in the status would be to help differentiate between a logical error with the request (a FaultException) and an unexpected error while processing of the request.

For an ongoing discussion of this topic, please check out this thread in the WCF MSDN Forum.

Leave a Reply