Convey FaultException details from an ajax-enabled WCF service?
<p>All of my services have the <em>enableWebScript</em> behaviour configuration, simply because I believe it's the most convenient behaviour to integrate with existing client applications. The applications consist of some relatively complex forms. Basic validation is performed on the client, and when the form data is received by the services, they perform a much more comprehensive validation. </p>

<p>So! When the services detect an erroneous data contract, they compile all validation errors in a <em>ValidationErrorsSummary</em> object and throws a <em>FaultException&lt;ValidationErrorsSummary&gt;</em>.</p>

<p>I would like the clients to be able to retrieve the <em>ValidationErrorsObject</em>, but since I haven't enabled <em>includeExceptionDetailInFaults</em>, they simply receive:</p>

<pre><code>{"ExceptionDetail":null,"ExceptionType":null,"Message":"The server was unable to process the request due to an internal error.  For more information about the error, either turn on IncludeExceptionDetailInFaults (either from ServiceBehaviorAttribute or from the &lt;serviceDebug&gt; configuration behavior) on the server in order to send the exception information back to the client, or turn on tracing as per the Microsoft .NET Framework SDK documentation and inspect the server trace logs.","StackTrace":null}
</code></pre>

<p>I've tried:</p>

<p>1) Create a custom error handler by implementing <em>IErrorHandler</em> but it doesn't seem to work in conjunction with <em>enableWebScript</em>. The code runs, but the response insists on defaulting to <em>202 Accepted</em> without a response body received by the client. I've tried both to create a <em>IServiceBehavior</em> attribute attached to the service itself, and by configuring the service behavior as a custom configuration element in <em>web.config</em>. </p>

<pre><code>public class ErrorWebHttpBehavior : WebHttpBehavior
{
    protected override void AddServerErrorHandlers(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
    {
        // clear default error handlers.
        endpointDispatcher.ChannelDispatcher.ErrorHandlers.Clear();

        // add our own error handler.
        endpointDispatcher.ChannelDispatcher.ErrorHandlers.Add(new ValidationErrorHandler());
    }
}

public class ValidationErrorBehaviorAttribute : Attribute, IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcherBase channelDispatcher in serviceHostBase.ChannelDispatchers)
        {
            var dispatcher = channelDispatcher as ChannelDispatcher;
            if (dispatcher != null)
                dispatcher.ErrorHandlers.Add(new ValidationErrorHandler());
        }
    }

    // ...
}

public class ValidationErrorHandler : IErrorHandler
{
    public bool HandleError(Exception error)
    {
        return true;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
        object details;

        var summary = error as FaultException&lt;ValidationErrorsSummary&gt;;
        if (error != null)
            details = summary.Detail.Errors;
        else
            details = "The server was unable to process the request due to an internal error.";

        // create a fault message containing our FaultContract object
        fault = Message.CreateMessage(version, "", details, new DataContractJsonSerializer(details.GetType()));

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

        // return custom error code.
        var rmp = new HttpResponseMessageProperty();
        rmp.StatusCode = System.Net.HttpStatusCode.BadRequest;
        rmp.StatusDescription = "Bad request";
        fault.Properties.Add(HttpResponseMessageProperty.Name, rmp);
    }
}

// ...

var summary = new ValidationErrorsSummary { Errors = validator.Errors };
throw new WebFaultException&lt;ValidationErrorsSummary&gt;(summary, System.Net.HttpStatusCode.BadRequest);
</code></pre>

<p>2) Intercept the request itself and use the <em>HttpContext</em> to <em>Response.Write</em> to the response stream. This doesn't work as the response is for some reason capped to 10 characters (even though I set the <em>Content-Length</em> to the length of the JSON string.</p>

<p>So what should I do? Any ideas? I <em>don't</em> want to convey arbitrary exception information, but I <em>really</em> want my validation summary to reach the clients.</p>

<hr>

<p>I've checked out the source code for <a href="http://referencesource.microsoft.com/#System.ServiceModel.Web/System/ServiceModel/Description/WebScriptEnablingBehavior.cs,acbbf5b66f4820af" rel="nofollow">WebScriptEnablingBehavior</a> and found this within the <em>JsonFaultBodyWriter</em> class:</p>

<pre><code>if (includeExceptionDetailInFaults)
{
    faultDetail.Message = fault.Reason.ToString();
    if (fault.HasDetail)
    {
        try
        {
            ExceptionDetail originalFaultDetail = fault.GetDetail&lt;ExceptionDetail&gt;();
            faultDetail.StackTrace = originalFaultDetail.StackTrace;
            faultDetail.ExceptionType = originalFaultDetail.Type;
            faultDetail.ExceptionDetail = originalFaultDetail;
        }
        // ...
    }
}
else
{
    faultDetail.Message = System.ServiceModel.SR.GetString(System.ServiceModel.SR.SFxInternalServerError);
}
</code></pre>

<p>I can make two assumptions by looking at the source code:</p>

<ul>
<li><p>For <em>ExceptionDetails</em> to be populated, you have to make sure that you throw a <em>FaultException&lt;ExceptionDetail&gt;</em> exception, <s>or at least, in my case, make sure that <em>ValidationErrorsSummary</em> inherits from that class.</s> This doesn't work because <em>GetDetail</em> use the serializer to retrieve the details, and as the custom derived class isn't a known type to the <em>ExceptionDetail</em> class, serialization fails. So you've got to stick with the base class.</p></li>
<li><p>It's impossible to receive information about the fault without enabling <em>includeExceptionDetailInFaults</em>. I mean, you aren't even in control of the error message.</p></li>
</ul>

<p>This is really bad. :(</p>