简体   繁体   中英

Convey FaultException details from an ajax-enabled WCF service?

All of my services have the enableWebScript 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.

So! When the services detect an erroneous data contract, they compile all validation errors in a ValidationErrorsSummary object and throws a FaultException<ValidationErrorsSummary> .

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

{"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 <serviceDebug> 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}

I've tried:

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

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<ValidationErrorsSummary>;
        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<ValidationErrorsSummary>(summary, System.Net.HttpStatusCode.BadRequest);

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

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


I've checked out the source code for WebScriptEnablingBehavior and found this within the JsonFaultBodyWriter class:

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

I can make two assumptions by looking at the source code:

  • For ExceptionDetails to be populated, you have to make sure that you throw a FaultException<ExceptionDetail> exception, or at least, in my case, make sure that ValidationErrorsSummary inherits from that class. This doesn't work because GetDetail use the serializer to retrieve the details, and as the custom derived class isn't a known type to the ExceptionDetail class, serialization fails. So you've got to stick with the base class.

  • It's impossible to receive information about the fault without enabling includeExceptionDetailInFaults . I mean, you aren't even in control of the error message.

This is really bad. :(

I've decided to abandon this idea due to the way the WebScriptEnablingBehavior class is implemented. I'll "exploit" the fact that it's a web service and convey the validation errors as a HTTP header instead X-Validation-Errors .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM