A Trick with Faults (Discussion)

The code yesterday was meant to motivate a side-discussion on how faults get generated and handled between the server and client proxy. If you tried running that sample, then you would have seen that despite the FaultException being thrown on the service, the service call completes normally. The return value of the service call is a fault message. If you've been writing your contracts with typed messages instead of the raw Message type, then this is the opposite behavior to what you're used to seeing. Using the same pattern for exception handling doesn't work between typed and untyped messages. This is particularly messy when you have a mix of typed and untyped operation contracts on the same service because it requires some duplicated logic for handling errors. However, I think that would be a pretty rare service design.

There are four cases that I think are interesting to look at so that you can see the different fault behaviors that could occur.

Untyped fault exception with an untyped message contract

The basic case from yesterday is to receive a fault message.

 <s:Envelope xmlns:s="www.w3.org/2003/05/soap-envelope" xmlns:a="www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">www.w3.org/2005/08/addressing/soap/fault</a:Action>
    <a:RelatesTo>urn:uuid:dd129ffe-a8ff-4a70-ad6f-ad48085e94e8</a:RelatesTo>
    <a:To s:mustUnderstand="1">www.w3.org/2005/08/addressing/anonymous</a:To>
  </s:Header>
  <s:Body>
    <s:Fault>
      <s:Code>
        <s:Value>s:Sender</s:Value>
      </s:Code>
      <s:Reason>
        <s:Text xml:lang="en-US">boo!</s:Text>
      </s:Reason>
    </s:Fault>
  </s:Body>
</s:Envelope>

Typed fault exception with an untyped message contract

I'm just changing the FaultException to a FaultException<string> here, although you can have any type you want for the fault detail. This changes the contents of the fault message but not the code path. Note that the action is different in addition to the detail section to match the parameterized type.

 <s:Envelope xmlns:s="www.w3.org/2003/05/soap-envelope" xmlns:a="www.w3.org/2005/08/addressing">
  <s:Header>
    <a:Action s:mustUnderstand="1">tempuri.org/IService/VerbStringFault</a:Action>
    <a:RelatesTo>urn:uuid:49ee87c7-691f-48c4-86ea-bb172c99294d</a:RelatesTo>
    <a:To s:mustUnderstand="1">www.w3.org/2005/08/addressing/anonymous</a:To>
  </s:Header>
  <s:Body>
    <s:Fault>
      <s:Code>
        <s:Value>s:Sender</s:Value>
      </s:Code>
      <s:Reason>
        <s:Text xml:lang="en-US">The creator of this fault did not specify a Reason.</s:Text>
      </s:Reason>
      <s:Detail>
        <string xmlns="schemas.microsoft.com/2003/10/Serialization/">boo!</string>
      </s:Detail>
    </s:Fault>
  </s:Body>
</s:Envelope>

Untyped fault exception with a typed message contract

As soon as we lose the untyped message (any return type but Message, even void), then we get an entirely different code path on the client. Instead of a return value, an exception of type FaultException gets thrown from the proxy.

 System.ServiceModel.FaultException: boo!
Server stack trace:
   at System.ServiceModel.Channels.ServiceChannel.HandleReply(ProxyOperationRuntime operation, ProxyRpc& rpc)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at IService.Verb(Message input)

Typed fault exception with a typed message contract

With typed messages, the fault exception type is now important for error handling. The parameterized server FaultException comes to the client as the same parameterized type (so FaultException), assuming that we've set up the fault contract for the service correctly. This means that we have the same basic code path but may be selecting a different case to apply.

 System.ServiceModel.FaultException`1[System.String]: The creator of this fault did not specify a Reason. (Fault Detail is equal to boo!).

Next time: Flow of Messages, Part 1

Comments

  • Anonymous
    February 28, 2007
    What does this code print? It seems like both choices are quite reasonable. I'll have some discussion

  • Anonymous
    March 02, 2007
    Harry wonders if it has been a slow week. It started that way for me but its certainly not now with both

  • Anonymous
    March 02, 2007
    Harry wonders if it has been a slow week. It started that way for me but its certainly not now with both