I'm implementing a RESTful web service using WCF and the WebHttpBinding. Currently I'm working on the error handling logic, implementing a custom error handler (IErrorHandler); the aim is to have it catch any uncaught exceptions thrown by operations and then return a JSON error object (including say an error code and error message - e.g. { "errorCode": 123, "errorMessage": "bla" }) back to the browser user along with an an HTTP code such as BadRequest, InteralServerError or whatever (anything other than 'OK' really). Here is the code I am using inside the ProvideFault method of my error handler:
fault = Message.CreateMessage(version, "", errorObject, new DataContractJsonSerializer(typeof(ErrorMessage)));
var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);
var rmp = new HttpResponseMessageProperty();
rmp.StatusCode = System.Net.HttpStatusCode.InternalServerError;
rmp.Headers.Add(HttpRequestHeader.ContentType, "application/json");
fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);
--> This returns with Content-Type: application/json, however the status code is 'OK' instead of 'InternalServerError'.
fault = Message.CreateMessage(version, "", errorObject, new DataContractJsonSerializer(typeof(ErrorMessage)));
var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);
var rmp = new HttpResponseMessageProperty();
rmp.StatusCode = System.Net.HttpStatusCode.InternalServerError;
//rmp.Headers.Add(HttpRequestHeader.ContentType, "application/json");
fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);
--> This returns with the correct status code, however the content-type is now XML.
fault = Message.CreateMessage(version, "", errorObject, new DataContractJsonSerializer(typeof(ErrorMessage)));
var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);
var response = WebOperationContext.Current.OutgoingResponse;
response.ContentType = "application/json";
response.StatusCode = HttpStatusCode.InternalServerError;
--> This returns with the correct status code and the correct content-type! The problem is that the http body now has the text 'Failed to load source for: http://localhost:7000/bla..' instead of the actual JSON data..
Any ideas? I'm considering using the last approach and just sticking the JSON in the HTTP StatusMessage header field instead of in the body, but this doesn't seem quite as nice?
In the latest version of WCF (As of 11/2011) there's a better way of doing this using WebFaultException. You can use it as follows in your service catch blocks:
Here's a complete solution based on some info from above:
Yes you have. You can create custom error handler and do what you feel like.
See the attached code.
That's the custom error handler:
That's an extended service behavior to inject the error handler:
That's a custom binding so you'll be able to configure it in the web.config
That's the web.config
Note: The behavior extension should be in one line EXACTLY as is (there's a bug in WCF).
That's my client side (part of our custom proxy)
I had the exact same problem. This was useful for me:
http://zamd.net/2008/07/08/error-handling-with-webhttpbinding-for-ajaxjson/
Double-check that your errorObject can be serialized by DataContractJsonSerializer. I ran into a problem where my contract implementation didn't provide a setter for one of the properties and was silently failing to serialize--resulting in similar symptoms: 'server did not send a response'.
Here's the code I used to get more details about the serialization error (makes a good unit test with an assertion and without the try/catch for breakpoint purposes):
Actually, this works for me.
Here's my ErrorMessage class:
Combined with the last snippet above:
This gives me proper errors as json. Thanks. :)
What does the ErrorMessage class look like?
Don't use the StatusMessage field for machine-readable data -- see http://tools.ietf.org/html/rfc2616#section-6.1.1 .
Also, it may be okay that "the http body now has the text 'Failed to load source for: http://localhost:7000/bla..' instead of the actual JSON data.." -- a literal string is JSON data if I remember correctly.