views:

1467

answers:

4

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?

A: 

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.

JLamb
A: 

Did you ever get a fix for this? I have the exact same problem

Update: This was useful for me: http://www.zamd.net/2008/07/08/ErrorHandlingWithWebHttpBindingForAjaxJSON.aspx

Christo Fur
Gives 404. The (new) url seems to be http://zamd.net/2008/07/08/error-handling-with-webhttpbinding-for-ajaxjson/
Tuomas Hietanen
+1  A: 

Actually, this works for me.

Here's my ErrorMessage class:

    [DataContract]
    public class ErrorMessage
    {
        public ErrorMessage(Exception error)
        {
            Message = error.Message;
            StackTrace = error.StackTrace;
            Exception = error.GetType().Name;
        }

        [DataMember(Name="stacktrace")]
        public string StackTrace { get; set; }
        [DataMember(Name = "message")]
        public string Message { get; set; }
        [DataMember(Name = "exception-name")]
        public string Exception { get; set; }
    }

Combined with the last snippet above:

        fault = Message.CreateMessage(version, "", new ErrorMessage(error), 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 gives me proper errors as json. Thanks. :)

Inferis
A: 

Hello all. I tried Inferis's answer and it's not working for me. My RESTful service successfully returns JSON upon success, but when there is an exception it returns HTML containing a description of the exception that occurred, rather than a JSON object representing my ErrorMessage object. I've implemented IErrorHandler and I can see that ProvideFault is getting called. I'm using the code that Inferis posted, and I can see that although it's successfully affecting the headers in the response, it doesn't affect the message--I'm still getting an HTML description of the error.

Can anyone recommend what else I should check? Or do I need to post more information?

Thanks in advance for any direction you can give!

Dave