当WCF方法抛出异常时,jQuery成功回调用空响应调用

前端之家收集整理的这篇文章主要介绍了当WCF方法抛出异常时,jQuery成功回调用空响应调用前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我在这个头上撕裂了我的头发,所以忍受我(这是一个很长的篇幅)。

基本信息

> ASP.NET 3.5与ASP.NET兼容模式下的WCF服务
>将jQuery与this service proxy用于AJAX请求
>自定义IErrorHandler和IServiceBehavior实现来捕获异常并提供Faults,将其序列化为JSON
我正在本地使用Cassini进行测试(我看到一些线程讨论了在本地调试时出现的问题,但在生产环境中工作正常)。

我遇到的问题是,当我的WCF服务抛出异常时,$ .ajax调用的成功处理程序被触发。响应为空,状态文本为“成功”,响应代码为202 / Accepted。

IErrorHandler实现确实被使用,因为我可以通过它来查看创建的FaultMessage。最后发生的是,成功回调会引发错误,因为当期望JSON字符串时,响应文本为空。错误回调从不触发。

提供一点洞察的一件事是从端点行为中删除enableWebScript选项。当我这样做时,发生了两件事情:

>回复不再包裹(即没有{d:“result”},只是“结果”)。
>错误回调被触发,但响应只是来自IIS的400 / Bad Request黄屏死机的HTML,而不是我的序列化故障。

我尝试了十大结果中显示的更多内容,或者来自Google的关于随机组合关键字“jquery ajax asp.net wcf faultcontract json”的更多内容,所以如果你计划在谷歌搜索一个答案,不要打扰。我希望有人在这个问题上遇到过这个问题。

最终我想要实现的是:

>能够在WCF方法中抛出任何类型的异常
>使用FaultContact
>捕获ShipmentServiceErrorHandler中的例外
>将序列化的ShipmentServiceFault(作为JSON)返回给客户端。
>调用错误回调,所以我可以处理项目4。

可能与:

> WCF IErrorHandler Extension not returning specified Fault

更新1

我检查了跟踪System.ServiceModel活动的输出,在调用UpdateCountry方法后的一个时刻,抛出异常,消息是

Server returned an invalid SOAP Fault.

就是这样一个内部的异常引发了串行器期望一个不同的根元素,但是我不能破译出很多其他的东西。

更新2

所以有了更多的烦恼,我有一些工作,虽然不是我认为理想的方式。这是我做的:

>删除< enableWebScript />选项从web.config的端点行为部分。
>从服务方法删除FaultContract属性
>实现了WebHttpBehavior的一个子类(称为ShipmentServiceWebHttpBehavior),并覆盖了AddServerErrorHandlers函数添加ShipmentServiceErrorHandler。
>更改了ShipmentServiceErrorHandlerElement以返回一个ShipmentServiceWebHttpBehavior类型的实例,而不是错误处理程序本身。
>移动< errorHandler />从web.config的服务行为部分到端点行为部分。

这不太理想,因为现在WCF忽略了我想要的服务方法的BodyStyle = WebMessageBodyStyle.WrappedRequest(尽管我现在可以完全省略它)。我也必须更改JS服务代理中的一些代码,因为它正在寻找一个包装({d:…})对象的响应。

以下是所有相关代码(ShipmentServiceFault对象非常自我说明)。

服务

我的服务很简单(截断版本):

[ServiceContract(Namespace = "http://removed")]
[AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
public class ShipmentService
{

    [OperationContract]
    [WebInvoke(Method = "POST",ResponseFormat = WebMessageFormat.Json,BodyStyle = WebMessageBodyStyle.WrappedRequest)]
    [FaultContract(typeof(ShipmentServiceFault))]
    public string UpdateCountry(Country country)
    {
        var checkName = (country.Name ?? string.Empty).Trim();
        if (string.IsNullOrEmpty(checkName))
            throw new ShipmentServiceException("Country name cannot be empty.");

        // Removed: try updating country in repository (works fine)

        return someHtml; // new country information HTML (works fine)
    }

}

错误处理

IErrorHandler,IServiceBehavior实现如下:

public class ShipmentServiceErrorHandlerElement : BehaviorExtensionElement
{
    protected override object CreateBehavior()
    {
        return new ShipmentServiceErrorHandler();
    }

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

public class ShipmentServiceErrorHandler : IErrorHandler,IServiceBehavior
{
    #region IErrorHandler Members

    public bool HandleError(Exception error)
    {
        // We'll handle the error,we don't need it to propagate.
        return true;
    }

    public void ProvideFault(Exception error,System.ServiceModel.Channels.MessageVersion version,ref System.ServiceModel.Channels.Message fault)
    {
        if (!(error is FaultException))
        {
            ShipmentServiceFault faultDetail = new ShipmentServiceFault
            {
                Reason = error.Message,FaultType = error.GetType().Name
            };

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

            this.ApplyJsonSettings(ref fault);
            this.ApplyHttpResponseSettings(ref fault,System.Net.HttpStatusCode.InternalServerError,faultDetail.Reason);
        }
    }

    #endregion

    #region JSON Exception Handling

    protected virtual void ApplyJsonSettings(ref Message fault)
    {
        // Use JSON encoding  
        var jsonFormatting = new WebBodyFormatMessageProperty(WebContentFormat.Json);

        fault.Properties.Add(WebBodyFormatMessageProperty.Name,jsonFormatting);
    }

    protected virtual void ApplyHttpResponseSettings(ref Message fault,System.Net.HttpStatusCode statusCode,string statusDescription)
    {
        var httpResponse = new HttpResponseMessageProperty()
        {
            StatusCode = statusCode,StatusDescription = statusDescription
        };

        httpResponse.Headers[HttpResponseHeader.ContentType] = "application/json";
        httpResponse.Headers["jsonerror"] = "true";

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

    #endregion

    #region IServiceBehavior Members

    public void AddBindingParameters(ServiceDescription serviceDescription,System.ServiceModel.ServiceHostBase serviceHostBase,System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints,System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
    {
        // Do nothing
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription,System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        IErrorHandler errorHandler = new ShipmentServiceErrorHandler();

        foreach (ChannelDispatcherBase channelDispatcherBase in serviceHostBase.ChannelDispatchers)
        {
            ChannelDispatcher channelDispatcher = channelDispatcherBase as ChannelDispatcher;

            if (channelDispatcher != null)
            {
                channelDispatcher.ErrorHandlers.Add(errorHandler);
            }
        }
    }

    public void Validate(ServiceDescription serviceDescription,System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        // Do nothing
    }

    #endregion
}

JavaScript

调用WCF方法开始于:

function SaveCountry() {
        var data = $('#uxCountryEdit :input').serializeBoundControls();
        ShipmentServiceProxy.invoke('UpdateCountry',{ country: data },function(html) {
            $('#uxCountryGridResponse').html(html);
        },onPageError);
    }

我之前提到的服务代理需要处理很多事情,但是核心是我们来到这里:

$.ajax({
    url: url,data: json,type: "POST",processData: false,contentType: "application/json",timeout: 10000,dataType: "text",// not "json" we'll parse
    success: function(response,textStatus,xhr) {

    },error: function(xhr,status) {                

    }
});

组态

我觉得这里的问题可能在这里,但是我已经尝试了每个组合的设置,我可以在网上找到一个例子。

<system.serviceModel>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
    <behaviors>
        <endpointBehaviors>
            <behavior name="Removed.ShipmentServiceAspNetAjaxBehavior">
                <webHttp />
                <enableWebScript />
            </behavior>
        </endpointBehaviors>
        <serviceBehaviors>
            <behavior name="Removed.ShipmentServiceServiceBehavior">
                <serviceMetadata httpGetEnabled="true"/>
                <serviceDebug includeExceptionDetailInFaults="false"/>
                <errorHandler />
            </behavior>
        </serviceBehaviors>
    </behaviors>
    <services>
        <service name="ShipmentService" behaviorConfiguration="Removed.ShipmentServiceServiceBehavior">
            <endpoint address="" 
                behaviorConfiguration="Removed.ShipmentServiceAspNetAjaxBehavior" 
                binding="webHttpBinding" 
                contract="ShipmentService" />
        </service>
    </services>
    <extensions>
        <behaviorExtensions>
            <add name="errorHandler" type="Removed.Services.ShipmentServiceErrorHandlerElement,Removed,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null"/>
        </behaviorExtensions>
    </extensions>
</system.serviceModel>

笔记

我注意到这个问题正在获得一些喜爱。我找到了解决这个问题的办法,希望在找到一段时间的时候提供一个答案。敬请关注!

解决方法

我不熟悉ASP或WCF,但我对jQuery非常熟悉。关于您的问题的一个事情是,您的服务正在返回202当抛出异常时成功。 jQuery根据从服务器返回的HTTP状态代码选择要调用哪个回调(成功或错误)。 202被认为是一个成功的响应,因此jQuery将会成功。如果你想让jQuery调用错误回调,你需要使你的服务返回一个40x或50x的状态代码。请咨询 http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html获取HTTP状态代码列表。

猜你在找的jQuery相关文章