Devolución de llamada de éxito jQuery llamada con respuesta vacía cuando el método WCF produce una excepción

Me estoy arrancando el pelo por este, así que tengan paciencia conmigo (es una publicación larga).

Información básicaASP.NET 3.5 con servicio WCF en modo de compatibilidad ASP.NETUsando jQuery coneste proxy de servicio para solicitudes AJAXPersonalizadoIErrorHandler yIServiceBehavior implementación para atrapar excepciones y proporcionar fallas, que se serializan en JSONEstoy probando localmente usando Cassini (he visto algunos subprocesos que hablan sobre problemas que ocurren al depurar localmente pero funcionan bien en un entorno de producción).

El problema con el que me encuentro es que cada vez que se lanza una excepción desde mi servicio WCF, eléxito controlador de la$.ajax la llamada se dispara. La respuesta está vacía, el texto de estado es "Correcto" y el código de respuesta es 202 / Aceptado.

losIErrorHandler la implementación se acostumbra porque puedo recorrerla y ver cómo se crea el FaultMessage. Lo que sucede al final es que elsuccess la devolución de llamada arroja un error porque el texto de respuesta está vacío cuando espera una cadena JSON. loserror la devolución de llamada nunca se activa.

Una cosa que proporcionó una pequeña idea fue eliminar elenableWebScript opción desde el comportamiento del punto final. Dos cosas sucedieron cuando hice esto:

Las respuestas ya no estaban envueltas (es decir, no{ d: "result" }, sólo"result")loserror la devolución de llamada se activa, pero la respuesta es solo el HTML para la pantalla amarilla de muerte 400 / Solicitud incorrecta de IIS, no mi error serializado.

He intentado tantas cosas como aparecen en los 10 primeros resultados o más de Google con respecto a combinaciones aleatorias de las palabras clave "jquery ajax asp.net wcf faultcontract json", así que si planeas buscar una respuesta en Google, no te molestes . Espero que alguien en SO se haya encontrado con este problema antes.

En definitiva, lo que quiero lograr es:

Poder lanzar cualquier tipo deException en un método WCFUtilizar unaFaultContactAtrapa las excepciones en elShipmentServiceErrorHandlerDevolver un serializadoShipmentServiceFault (como JSON) al cliente.Tener elerror devolución de llamada invocada para que pueda manejar el elemento 4.

Posiblemente relacionado con:

WCF IErrorHandler Extension no devuelve la falla especificada

Actualización 1

Examiné el resultado del seguimiento de la actividad de System.ServiceModel, y en un momento después de llamar al método UpdateCountry, se produce una excepción, el mensaje es

El servidor devolvió una falla SOAP no válida.

y eso es. Una excepción interna se queja de que el serializador espera un elemento raíz diferente, pero no puedo descifrar mucho más de él.

Actualización 2

Entonces, con un poco más de juego, obtuve algo para trabajar, aunque no de la manera que consideraría ideal. Esto es lo que hice:

Retiró el<enableWebScript /> opción de la sección de comportamiento de punto final de web.config.Retiró elFaultContract atributo del método de servicio.Implementado una subclase deWebHttpBehavior (llamadoShipmentServiceWebHttpBehavior) y anuló elAddServerErrorHandlers función para agregar elShipmentServiceErrorHandler.Cambiado elShipmentServiceErrorHandlerElement para devolver una instancia de tipo deShipmentServiceWebHttpBehavior en lugar del controlador de errores en sí.Movido el<errorHandler /> línea desde la sección de comportamiento del servicio de web.config hasta la sección de comportamiento del punto final.

No es ideal porque ahora WCF ignora elBodyStyle = WebMessageBodyStyle.WrappedRequest Quiero en mis métodos de servicio (aunque ahora puedo omitirlo por completo). También tuve que cambiar parte del código en el proxy de servicio JS porque estaba buscando un contenedor ({ d: ... }) objetar sobre las respuestas.

Aquí está todo el código relevante (elShipmentServiceFault El objeto se explica por sí mismo).

El servicio

Mi servicio es muy simple (versión truncada):

[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)
    }

}
Manejo de errores

losIErrorHandler, IServiceBehavior la implementación es la siguiente:

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
}
El JavaScript

Llamar al método WCF comienza con:

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

El proxy de servicio que mencioné anteriormente se encarga de muchas cosas, pero en esencia, llegamos aquí:

$.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) {                

    }
});
Configuración

Siento que los problemas pueden estar aquí, pero he probado casi todas las combinaciones de configuraciones de todas partes que puedo encontrar en la red que tienen un ejemplo.

<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>

Notas

Noté que estas preguntas están obteniendo algunos favoritos. yohizo encuentre la solución a este problema y espero dar una respuesta cuando encuentre algo de tiempo. ¡Manténganse al tanto!

Respuestas a la pregunta(5)

Su respuesta a la pregunta