WCF: proporcione FaultException genérico en IErrorHandler
Algún contexto: tenemos un XSD personalizado y generamos el código WSDL y C # usando WSCF.blue. El lado del cliente usaChannelFactory<T>
y comparte la interfaz que incluye todos los atributos agregados por WSCF.blue para que coincida con lo que está en el XSD.
Estoy tratando de implementarIErrorHandler.ProvideFault
donde proporciona un genéricoFaultException<T>
, pero en el lado del cliente estoy obteniendo un no genéricoFaultContract
. Esto es lo que miProvideFault
El método se ve así:
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
if (!(error is FaultException))
{
FaultException faultException = FaultExceptionFactory.CreateFaultException(error);
MessageFault messageFault = faultException.CreateMessageFault();
fault = Message.CreateMessage(version, messageFault, faultException.Action);
}
}
En cada método de servicio, si hago un try / catch conthrow FaultExceptionFactory.CreateFaultException(ex)
funciona como se esperaba, así que creo que el[FaultContract]
, fábrica, enlaces, etc. son todos correctos. Por si acaso, así es como funciona la fábrica:
BusinessRuleFaultExceptionType businessRuleFaultException = new BusinessRuleFaultExceptionType();
BusinessRuleFaultException.Code = exception.Code.ToString();
BusinessRuleFaultException.Reason = exception.Message;
return new FaultException<BusinessRuleFaultExceptionType>(
businessRuleFaultException,
exception.Message,
new FaultCode(exception.Code.ToString())
);
Creo que el problema es cómo se crea el mensaje enIErrorHandler
, tal vez enCreateMessageFault()
. He leído que la acción debería serfaultException.Action
en lugar denull
, Pero, de hechofaultException.Action
esnull
. Quizás esto esté causando el problema. Puedo establecer una acción en la fábrica, pero ¿cuál debería ser la acción y por qué no aparecería eso con el lanzamiento manual?
¿Alguna otra idea de lo que podría estar perdiendo?
Editar: Revisé el WSDL y encontré la operación específica que estaba llamando y su acción:
<wsdl:operation name="MyMethod">
<wsdl:fault wsaw:Action="http://myNamespace/MyMethodBusinessRuleFaultExceptionTypeFault" name="BusinessRuleFaultExceptionTypeFault" message="tns:..."/>
Traté de codificar la acción:Message.CreateMessage(..., "http://myNamespace/MyMethodBusinessRuleFaultExceptionTypeFault")
y poniéndolo a la.Action
en la fábrica pero eso todavía no funcionó.
Edición 2: Throw / catch genera el siguiente XML que permite detectar la excepción genérica en el cliente:
<s:Fault>
<faultcode xmlns="">s:-100</faultcode>
<faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
<detail xmlns="">
<BusinessRuleFaultExceptionType xmlns="http://myNamespace/Services" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Code xmlns="http://myNamespace/Entitites">-100</Code>
<Reason xmlns="http://myNamespace/Entitites">xxx</Reason>
</BusinessRuleFaultExceptionType>
</detail>
</s:Fault>
losIHttpErrorHandler
genera lo siguiente que va al no genéricoFaultException
:
<s:Fault>
<faultcode xmlns="">s:-100</faultcode>
<faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
<detail xmlns="">
<BusinessRuleFaultExceptionType xmlns="http://schemas.datacontract.org/2004/07/My.Dot.Net.Namespace" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<codeField>-100</codeField>
<reasonField>xxx</reasonField>
</BusinessRuleFaultExceptionType>
</detail>
</s:Fault>
Edición 3: Si agrego[DataContract]
y[DataMember]
alBusinessRuleFaultExceptionType
, entonces obtengo casi el XML correcto:
<s:Fault>
<faultcode xmlns="">s:-100</faultcode>
<faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
<detail xmlns="">
<BusinessRuleFaultExceptionType xmlns="http://myNamespace/Services" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<Code>-100</Code>
<Reason>xxx</Reason>
</BusinessRuleFaultExceptionType>
</detail>
</s:Fault>
Le falta el espacio de nombres para Código y razón. Al menos creo que esto lo reduce. La serialización regular está utilizando el[XmlType]
y[XmlElement]
mientras que laIErrorHandler
esta usando[DataContract]
y[DataMember]
. Desafortunadamente[DataMember]
no le permite configurar el espacio de nombres, por lo que creo que la pregunta ahora es cómo usar XMLSerializer enIErrorHandler
. Esto describe mi problema, pero la solución no funcionará por la razón indicada anteriormente:http://twenty6-jc.blogspot.com/2011/05/ierrorhandlerprovidefault-serialization.html
Edición 4: Encontré parcialmente el problema. Estamos usandoXmlSerializer
, pero desdeIErrorHandler
está fuera del alcance de la operación, vuelve al valor predeterminadoDataContractSerializer
. La solución es cambiar su servicio para usarDataContractSerializer
en todas partes, o elegir manualmenteXmlSerializer
al crear las fallas. Estos dos artículos proporcionaron lo que necesitaba:
http://twenty6-jc.blogspot.com/2011/05/ierrorhandlerprovidefault-serialization.html http://zamd.net/2008/08/15/serializing-faults-using-xmlserializer/
Eso me atrapamuy cerrar. Es lo mismo que el XML de trabajo, excepto que falta el espacio de nombres del tipo de excepción:
<s:Fault>
<faultcode xmlns="">s:-100</faultcode>
<faultstring xml:lang="en-US" xmlns="">xxx</faultstring>
<detail xmlns="">
<BusinessRuleFaultExceptionType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Code xmlns="http://myNamespace/Entitites">-100</Code>
<Reason xmlns="http://myNamespace/Entitites">xxx</Reason>
</BusinessRuleFaultExceptionType>
</detail>
</s:Fault>
Supongo que no agregaxmlns="http://myNamespace/Services"
porque no tiene el contexto de la solicitud. Ese espacio de nombres se define en la interfaz, pero no en el contrato de datos. ¿Realmente me veré obligado a permanecer dentro del contexto de la solicitud?IOperationInvoker
funciona), en lugar de usarIHttpHandler
?