WCF: forneça FaultException genérica em IErrorHandler
Algum contexto: Temos um XSD personalizado e geramos o código WSDL e C # usando WSCF.blue. O lado do cliente usaChannelFactory<T>
e compartilha a interface que inclui todos os atributos adicionados pelo WSCF.blue para corresponder ao que está no XSD.
Estou tentando implementarIErrorHandler.ProvideFault
onde ele fornece um genéricoFaultException<T>
, mas no lado do cliente estou recebendo de volta um não genéricoFaultContract
. Isto é o que meuProvideFault
O método se parece com:
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);
}
}
Em cada método de serviço, se eu tentar / capturar comthrow FaultExceptionFactory.CreateFaultException(ex)
funciona como esperado, então eu acho que o[FaultContract]
, fábrica, ligações, etc. estão todos corretos. Por precaução, é assim que a fábrica funciona:
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())
);
Eu acho que o problema é como a mensagem é criada emIErrorHandler
, talvez emCreateMessageFault()
. Eu li que a ação deve serfaultException.Action
ao invés denull
, mas de fatofaultException.Action
énull
. Talvez isso esteja causando o problema. Posso definir uma ação na fábrica, mas qual deve ser a ação e por que isso não aparece com o lançamento manual?
Alguma outra idéia do que eu possa estar perdendo?
Editar: Eu verifiquei o WSDL e encontrei a operação específica que estava chamando e sua ação:
<wsdl:operation name="MyMethod">
<wsdl:fault wsaw:Action="http://myNamespace/MyMethodBusinessRuleFaultExceptionTypeFault" name="BusinessRuleFaultExceptionTypeFault" message="tns:..."/>
Eu tentei codificar a ação:Message.CreateMessage(..., "http://myNamespace/MyMethodBusinessRuleFaultExceptionTypeFault")
e configurá-lo para o.Action
na fábrica, mas isso ainda não funcionou.
Edição 2: O throw / catch gera o seguinte XML que permite capturar a exceção genérica no 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>
oIHttpErrorHandler
gera o seguinte que vai para o não 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>
Edição 3: Se eu adicionar[DataContract]
e[DataMember]
aoBusinessRuleFaultExceptionType
, então recebo quase o XML correto:
<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>
Está faltando o espaço para nome de Código e Razão. Pelo menos eu acho que isso diminui. A serialização regular está usando o[XmlType]
e[XmlElement]
enquanto oIErrorHandler
está usando[DataContract]
e[DataMember]
. Infelizmente[DataMember]
não permite definir o espaço para nome, então acho que a questão agora é como usar o XMLSerializer noIErrorHandler
. Isso descreve meu problema, mas a correção não funcionará pelo motivo indicado acima:http://twenty6-jc.blogspot.com/2011/05/ierrorhandlerprovidefault-serialization.html
Edição 4: Encontrei parcialmente o problema. Nós estamos usandoXmlSerializer
, mas desdeIErrorHandler
está fora do escopo da operação, ele volta ao padrãoDataContractSerializer
. A solução é alterar seu serviço para usarDataContractSerializer
em qualquer lugar ou escolha manualmenteXmlSerializer
ao criar as falhas. Esses dois artigos forneceram o que eu precisava:
http://twenty6-jc.blogspot.com/2011/05/ierrorhandlerprovidefault-serialization.html http://zamd.net/2008/08/15/serializing-faults-using-xmlserializer/
Isso me deixamuito Fechar. É o mesmo que o XML de trabalho, exceto que o namespace do tipo de exceção está ausente:
<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>
Presumo que não adicionexmlns="http://myNamespace/Services"
porque não possui o contexto da solicitação. Esse espaço para nome é definido na interface, mas não o contrato de dados. Eu realmente serei forçado a permanecer dentro do contexto da solicitação (esperoIOperationInvoker
funciona), em vez de usarIHttpHandler
?