views:

155

answers:

3

Hopefully there are some WCF wizards out there that can spot my mistake here.

I am trying to set up a global error handler via an IErrorHandler based behaviorExtension on a RESTful JSON WCF Service. The method is decorated as such:

[OperationContract]
[WebGet(UriTemplate = "screens/info", ResponseFormat = WebMessageFormat.Json)]

The IErrorHandler implementation is:

public class ErrorHandler : IErrorHandler
{
    public void ProvideFault(Exception error, 
                             MessageVersion version, 
                             ref Message fault)
    {
        var error = new JsonError 
                        { 
                            Message = error.Message, 
                            FaultCode = -1, 
                            StackTrace = error.StackTrace
                        };

        fault = Message.CreateMessage(version, 
                    "", 
                    ideaScreeningError, 
                    new DataContractJsonSerializer(
                        ideaScreeningError.GetType()));

        // tell WCF to use JSON encoding rather than default XML
        var wbf = new WebBodyFormatMessageProperty(WebContentFormat.Json);
        fault.Properties.Add(WebBodyFormatMessageProperty.Name, wbf);

        //Modify response
        var rmp = new HttpResponseMessageProperty 
                      { 
                          StatusCode = HttpStatusCode.BadRequest, 
                          StatusDescription = "Bad Request"
                      };

        fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);
    }

    public bool HandleError(Exception error)
    {
        return true;
    }
}

I can verify (via breakpoints) that the extension is being called and is executing properly. When I look at the result of the AJAX call in the browser, I can see that WCF is still returning a 500 Internal Server Error rather than the Fault details that I've specified in the error handler.

If I change Exception types being thrown in the WCF method, those are reflected in the result in the browser so I can surmise that WCF is doing something to handle the Exception and return something internally.

How do I make it stop!?

EDIT

I'm adding the custom Behavior Element:

public class ErrorBehaviorElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new ErrorBehavior();
    }

    public override Type BehaviorType
    {
        get { return typeof(ErrorBehavior); }
    }
}

And Behavior:

internal class ErrorBehavior : WebHttpBehavior
{
    protected override void AddServerErrorHandlers(ServiceEndpoint endpoint,
        EndpointDispatcher endpointDispatcher)
    {
        // clear default error handlers.  
        endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();

        // add the Json error handler.  
        endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(
            new ErrorHandler());
    }  
}
A: 

Don't forget to set the ContentType of the response as well:

rmp.Headers[HttpResponseHeader.ContentType] = "application/json";
Johann Blais
A: 

Based on comments I would try to remove common webHttpBehavior. You have defined your own behavior derived from webHttp. There is no reason to have two webHttp behaviors in your service configuration. Moreover webHttp behavior adds its own error handler which behaves exactly as you describe. Maybe it will not help but you can give it a try.

Ladislav Mrnka
I gave this a shot. Oddly enough, when I remove the webHttpBehavior, my extension is no longer called to handle the Exception.
Justin Niessner
I figured out what the issue was (finally). It was something totally unexpected. I posted the solution for you to check out.
Justin Niessner
+3  A: 

The issue here lies with the WCF Rest Starter Kit (which I didn't realize was in use since I didn't start this project), more specifically WebServiceHost2. I opened the ServiceHost in Reflector and found this lovely little piece of code in OnOpening():

if (endpoint.Behaviors.Find<WebHttpBehavior>() != null)
{
    endpoint.Behaviors.Remove<WebHttpBehavior>();
    WebHttpBehavior2 item = new WebHttpBehavior2();
    // other code omitted
    endpoint.Behaviors.Add(item);
}

As you can see, no matter what behavior you want added to the endpoint, as long as it inherits from WebHttpBehavior the Rest Start Kit components will hijack your handler, remove it, and replace it with its own.

Keep in mind that WebHttpBehavior2 also inherits from WebHttBehavior so inheriting from WebHttpBehavior2 in my extension did nothing to help the matter.

The first step was to create a new WebSeriveHost that derived from WebServiceHost2 and overrode OnOpening() and re-hijack what the Rest Starter Kit stole from me:

if(endpoint.Behaviors.Find<WebHttpBehavior>() != null)
{
    endpoint.Behaviors.Remove<WebHttpBehavior>();
    var item = ErrorBehavior();
    // other code
    endpoint.Behaviors.Add(item);
}

And then create a new WebServiceHostFactory that returned my custom WebServiceHost type.

Justin Niessner
Nice. I hate such features which simply change the logic without even telling you. Thank God we have Reflector.
Ladislav Mrnka