- Doing Business with SkatesTown
- Inventory Check Web Service
- A Closer Look at SOAP
- The SOAP Messaging Framework
- SOAP Intermediaries
- The SOAP Body
- The SOAP Processing Model
- Versioning in SOAP
- Processing Headers and Bodies
- Faults: Error Handling in SOAP
- Objects in XML: The SOAP Data Model
- The SOAP RPC Conventions
- XML, Straight Up: Document-Style SOAP
- When to Use Which Style
- The Transport Binding Framework
- Using SOAP to Send Binary Data
- Small-Scale SOAP, Big-Time SOAP
Faults: Error Handling in SOAP
When something goes wrong in Java, we expect someone to throw an exception; the exception mechanism gives us a common framework with which to deal with problems. The same is true in the SOAP world. When a problem occurs, the SOAP spec provides a well-known way to indicate what has happened: the SOAP fault. Let's look at an example fault message:
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:st="http://www.skatestown.com/ws"> <env:Header> <st:PublicServiceAnnouncement> Skatestown's Web services will be unavailable after 5PM today for a two hour maintenance window. </st:PublicServiceAnnouncement> </env:Header> <env:Body> <env:Fault> <env:Code> <env:Value>env:Sender</env:Value> <env:Subcode> <env:Value>st:InvalidPurchaseOrder</env:Value> </env:Subcode> </env:Code> <env:Reason> <env:Text xml:lang="en-US"> Your purchase order did not validate! </env:Text> </env:Reason> <env:Detail> <st:LineNumber>9</st:LineNumber> <st:ColumnNumber>24</st:ColumnNumber> </env:Detail> </env:Fault> </env:Body> </env:Envelope>
Structure of a Fault
A SOAP fault message is a normal SOAP message with a single, well-known element inside the body: soapenv:Fault. The presence of that element acts as a signal to processors to indicate something has gone wrong. Of course, just knowing something is wrong is rarely useful enough; you need a structure to help determine what happened so you can either try again with a better idea of what might work or let the user know the problem. SOAP faults have several components to help in this regard.
The fault code is the first place to look, since it tells you in a general sense what the problem was. Fault codes are QNames, and SOAP defines the set of legal codes as follows (each item is the local part of the QNamethe namespace is always the SOAP envelope namespace):
SenderThe problem was caused by incorrect or missing data from the sender. For instance, if a service required a security header in order to do its work and it was called without one, it would generate a Sender fault. You typically have to make a change to your message before resending it if you hope to be successful.
ReceiverSomething went wrong on the receiver while processing the message, but it wasn't directly attributable to the message contents. For example, a necessary resource like a database was down, a thread wasn't available, and so on. A message causing a Receiver fault might succeed if resent at a later time.
mustUnderstandThis fault code indicates that a header was received that was targeted at the receiving node, marked mustUnderstand="true", and not understood.
VersionMismatchThe VersionMismatch code is generated when the namespace on the SOAP envelope that was received isn't compatible with the SOAP version on the receiver. This is the way SOAP handles protocol versioning; we'll talk about it in more detail later.
The fault code resides inside the Code element in the fault, in a subelement called Value. In the example code, you can see the Sender code, meaning something must have been wrong with the request that caused this fault. We have the Value element instead of putting the code qname directly inside the Code element so that we can extend the expressive space of possible fault codes by adding more data inside another element, Subcode.
SOAP 1.2 lets you specify an arbitrary hierarchy of fault subcodes, which provide further detail about what went wrong. The syntax is a little verbose, but it works. Here's an example:
<env:Code> <env:Value>env:Sender</env:Value> <env:Subcode> <env:Value>st:InvalidPurchaseOrder</env:Value> </env:Subcode> </env:Code>
The Code element contains an optional Subcode element. Just as Code contains a mandatory Value, so too does each Subcodeand each Subcode may contain another Subcode, to whatever level of nesting is desired. Generally the hierarchy won't go more than about three levels deep. In our example, the subcode tells us that the problem was an invalid purchase order.
The Reason element, also required, contains one or more human-readable descriptions of the fault condition. Typically, the reason text might appear in a dialog box that alerts the user of a problem, or it might be written into a log file. The Text element contains the text and there can be one or more such messages. Why would you have more than one? In the increasingly international environment of the Web, you might wish to send the fault description in several languages, as in this example from the SOAP primer:
<env:Reason> <env:Text xml:lang="en-US">Processing error</env:Text> <env:Text xml:lang="cs">Chyba zpracování</env:Text> </env:Reason>
The spec states that if you have multiple Text elements, you should have a different value for xml:lang in each oneotherwise you might confuse the software that's trying to print out a single coherent message in a given language.
Node and Role
The optional Node element, not shown in our example, tells us which SOAP node (the sender, an intermediary, or the ultimate destination) was processing the message at the time the fault occurred. It contains a URI.
The Role element tells which role the faulting node was playing when the fault occurred. It contains a URI that has exactly the same semantics, and the same values, as the role attribute we described when we were talking about headers. Note the difference between this element and NodeNode tells you which SOAP node generated the fault, and Role tells what part that node was playing when it happened. The Role element is also optional.
We have a custom fault code and a fault message, both of which can tell a user or software something about the problem; but in many cases, we would also like to pass back some more complex machine-readable data. For example, you might want to include a stack trace while you're developing services to aid with debugging (though you likely wouldn't do this in a production application, since stack traces can sometimes give away information that might be useful to someone trying to compromise your system).
You can place anything you want inside the SOAP fault's Detail element. In our example at the beginning of the section, the line number and column number where the validation error occurred are expressed, so that automated tools might be able to help the user or developer to fix the structure of the transmitted message.
SOAP 1.1 Difference: Handling Faults
Faults in SOAP 1.2 got an overhaul from SOAP 1.1's version. All the subelements of the SOAP Fault element in SOAP 1.1 are unqualified (in no namespace). The Fault subelements in SOAP 1.2 are in the envelope namespace.
In SOAP 1.1, there is no Subcode, only a single faultcode element. The SOAP 1.1 fault code is a QName, but its hierarchy is achieved through dots rather than explicit structurein other words, whereas in SOAP 1.1 you might have seen
<faultcode>env:Sender.Authorization.BadPassword</faultcode> in SOAP 1.2 you see something like: <env:Code> <env:Value>env:Sender</env:Value> <env:Subcode> <env:Value>myNS:Authorization</env:Value> <env:Subcode> <env:Value>myNS:BadPassword</env:Value> </env:Subcode> </env:Subcode> </env:Code>
The env:Reason element in SOAP 1.2 is called faultstring in SOAP 1.1. Also, 1.1 only allows a single string inside faultstring, whereas 1.2 allows different env:Text elements inside env:Reason to account for different languages.
The Client fault code from 1.1 is now Sender, which is less prone to interpretation. Similarly, 1.1's Server fault code is now Receiver.
In SOAP 1.1, the detail element is used only for information pertaining to faults generated when processing the SOAP body. If a fault is generated when processing a header, any machine-readable information about the fault must travel in headers on the fault message. The reasoning for this went something like this: Headers exist so that SOAP can support orthogonal extensibility; that means you want a given message to be able to carry several extensions that might not have been designed by the same people and might have no knowledge of each other. If problems occurred that caused each of these extensions to want to pass back data, they might have to fight for the detail element. The problem with this logic is that the detail element isn't a contended resource, in the same way the soapenv:Header isn't a contended resource. If multiple extensions want to drop their own elements into detail, that works just as well as putting their own headers into the envelope. So this restriction was dropped in SOAP 1.2, and env:Detail can contain anything your application desiresbut the rule still must be followed for SOAP 1.1.
SOAP 1.2 introduces the NotUnderstood header and the Upgrade header, both of which exist in order to clarify what went wrong with particular faults (mustUnderstand and VersionMismatch) in a standard way.
Using Headers in Faults
Since a fault is also a SOAP message, it can carry SOAP headers as well as the fault structure. In our example at the beginning of this section, you can see that SkatesTown has included a public service announcement header. This optional information lets anyone who cares know that the Web services will be down for maintenance; and since it isn't marked mustUnderstand, it doesn't affect the processing of the fault message in any way. SOAP defines some headers specifically for use in faults.
The NotUnderstood Header
You'll recall that SOAP processors are forced to fault if they encounter a mustUnderstand header that they should process but don't understand. It's great to know something wasn't understood, but it's more useful if you have an indication of which header was the cause of the problem. That way you might be able to try again with a different message if the situation warrants. For example, let's say a message was sent with a routing header marked mustUnderstand="true". The purpose of the routing header is to let the service know that after it finishes processing the message, it's supposed to send a copy to an endpoint whose address is in the contents of the header (probably for logging purposes). If the receiver doesn't understand the header, it sends back a mustUnderstand fault. The sender might then, for instance, ask the user if they would still like to send the message, but without the carbon-copy functionality. If the routing header is the only one in the envelope, then it's easy to know which header the mustUnderstand fault refers to. But what if there are multiple mustUnderstand headers?
SOAP 1.2 introduced a NotUnderstood header to deal with this issue. When sending back a mustUnderstand fault, SOAP endpoints should include a NotUnderstood header for each header in the original message that was not understood. The NotUnderstood header (in the SOAP envelope namespace) has a qname attribute containing the QName of the header that wasn't understood. For example:
<env:Envelope xmlns:env='http://www.w3.org/2003/05/soap-envelope'> <env:Header> <abc:Extension1 xmlns:abc='http://example.org/2001/06/ext' env:mustUnderstand='true'/> <def:Extension2 xmlns:def='http://example.com/stuff' env:mustUnderstand='true' /> </env:Header> <env:Body> . . . </env:Body> </env:Envelope>
If a processor received this message and didn't understand Extension1 but did understand Extension2, it would return a fault like this:
<env:Envelope xmlns:env='http://www.w3.org/2003/05/soap-envelope' xmlns:xml='http://www.w3.org/XML/1998/namespace'> <env:Header> <env:NotUnderstood qname='abc:Extension1' xmlns:abc='http://example.org/2001/06/ext' /> </env:Header> <env:Body> <env:Fault> <env:Code> <env:Value>env:mustUnderstand</env:Value> </env:Code> <env:Reason> <env:Text xml:lang='en'>One or more mandatory SOAP header blocks not understood </env:Text> </env:Reason> </env:Fault> </env:Body> </env:Envelope>
This information is handy when you're trying to use the SOAP extensibility mechanism to negotiate QoS or policy agreements between communicating parties.
The Upgrade Header
Back in the section on versioning, we mentioned the Upgrade header, which SOAP 1.2 defines as a standard mechanism for indicating which versions of SOAP are supported by a node generating a VersionMismatch fault. This section fully defines this header.
An Upgrade header (which actually is a misnomerit doesn't always imply an upgrade in terms of using a more recent version of the protocol) looks like this in context:
<?xml version="1.0" ?> <env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:xml="http://www.w3.org/XML/1998/namespace"> <env:Header> <env:Upgrade> <env:SupportedEnvelope qname="ns1:Envelope" xmlns:ns1="http://www.w3.org/2003/05/soap-envelope"/> <env:SupportedEnvelope qname="ns2:Envelope" xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/"/> </env:Upgrade> </env:Header> <env:Body> <env:Fault> <env:Code> <env:Value>env:VersionMismatch</env:Value> </env:Code> <env:Reason> <env:Text xml:lang="en">Version Mismatch</env:Text> </env:Reason> </env:Fault> </env:Body> </env:Envelope>
This fault would be generated by a node that supports both SOAP 1.1 and SOAP 1.2, in response to some envelope in another namespace. The Upgrade header, in the SOAP envelope namespace, contains one or more SupportedEnvelope elements, each of which indicates the QName of a supported envelope element. The SupportedEnvelope elements are ordered by preference, from most preferred to least. Therefore, the previous fault indicates that although this node supports both SOAP 1.1 and 1.2, 1.2 is preferred.
All the VersionMismatch faults we've shown so far use SOAP 1.2. However, if a SOAP 1.1 node doesn't understand SOAP 1.2, it won't be able to parse a SOAP 1.2 fault. As such, SOAP 1.2 specifies rules for responding to SOAP 1.1 messages from a node that only supports SOAP 1.2. It's suggested that such nodes recognize the SOAP 1.1 namespace and respond with a SOAP 1.1 version mismatch fault containing an Upgrade header as specified earlier. That way, nodes that have the capability to switch to SOAP 1.2 will know to do so, and nodes that can't do so will still be able to understand the fault as a versioning problem.