从启用 ajax 的 WCF 服务传送 FaultException 详细信息?

Convey FaultException details from an ajax-enabled WCF service?

我的所有服务都有 enableWebScript 行为配置,只是因为我相信这是与现有客户端应用程序集成的最方便的行为。应用程序由一些相对复杂的表格组成。基本验证在客户端执行,当服务接收到表单数据时,它们会执行更全面的验证。

所以!当服务检测到错误的数据协定时,它们会在 ValidationErrorsSummary 对象中编译所有验证错误并抛出 FaultException.

我希望客户端能够检索 ValidationErrorsObject,但由于我没有启用 includeExceptionDetailInFaults,他们只是接收:

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

我试过:

1) 通过实施 IErrorHandler 创建自定义错误处理程序,但它似乎无法与 enableWebScript 结合使用。代码运行,但响应坚持默认为 202 Accepted,而客户端没有收到响应正文。我已经尝试创建附加到服务本身的 IServiceBehavior 属性,并通过将服务行为配置为 web.config[= 中的自定义配置元素66=]。

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) 拦截请求本身并使用 HttpContextResponse.Write 到响应流。这不起作用,因为由于某种原因响应被限制为 10 个字符(即使我将 Content-Length 设置为 JSON 字符串的长度。

那我该怎么办?有任何想法吗?我不想传达任意异常信息,但我真的希望我的验证摘要能够到达客户。


我检查了 WebScriptEnablingBehavior 的源代码并在 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);
}

我可以通过查看源代码做出两个假设:

  • 要填充 ExceptionDetails,您必须确保抛出 FaultException 异常, 或者至少,就我而言,确保 ValidationErrorsSummary 继承自 class。 这不起作用,因为 GetDetail 使用序列化程序检索详细信息,并且由于自定义派生 class 不是 ExceptionDetail [=94= 的已知类型], 序列化失败。所以你必须坚持基本 class.

  • 如果不启用 includeExceptionDetailInFaults,则无法接收有关故障的信息。我的意思是,您甚至无法控制错误消息。

这真的很糟糕。 :(

由于 WebScriptEnablingBehavior class 的实现方式,我决定放弃这个想法。我将 "exploit" 这是一个网络服务这一事实,并将验证错误作为 HTTP header 而不是 X-Validation-Errors.