SOAP

Anatomy of a SOAP Message

SOAP provides a standard way of packaging a message. A SOAP message is composed of an envelope that contains the body of the message and any header information used to describe the message. Here is an example:

The root element of the document is the Envelope element. The example contains two subelements, the Body and Header elements. A valid SOAP message can also contain other child elements within the envelope. You will see examples of this when I discuss serializing references using SOAP Encoding.

The envelope can contain an optional Header element, which contains information about the message. In the preceding example, the header contains two elements describing the individual who composed the message and the intended recipient of the message. (I describe the SOAP header in more detail later in the chapter.)

The envelope must contain one Body element. The body contains the message payload. In my example, the body contains a simple character string.

Notice that each SOAP-specific element has the soapnamespace prefix. This prefix is defined within the Envelope element and points to the SOAP schema that describes the structure of a SOAP message. The prefix is appended to any elements defined within the SOAP namespace. These elements are fully qualified. The soap prefix indicates that the Envelope element is an instance of the SOAP Envelope type. I will drill deeper into XML namespaces in the next chapter.

SOAP Actors

Before I describe the individual parts of a SOAP message, I want to define a couple of terms I will be using. A SOAP actor is anything that acts on the content of the SOAP message. There are two types of SOAP actors, default actors and intermediaries.

The default actor is the intended final recipient of a SOAP message. An intermediary receives a SOAP message and might act on the message (including modifying it in some way) before forwarding it along the intended message path, as shown in the following diagram. Even though intermediaries might modify the data transferred from the client to the default actor, it is still considered the same message.

The Header Element

The optional Header element is used to pass data that might not be appropriate to encode in the body. For example, if the default actor receives a message in which the body is compressed, the default actor would need to know what type of compression algorithm was used in order to uncompress the message. Embedding information about the compression algorithm into the body does not make sense because the body itself will be compressed. Placing this type of information in the header of the message is more appropriate.

Other uses for the header include the following:

  • Authentication. The recipient might require the sender to authenticate himself before the message can be processed.

  • Security digest information. If the recipient needs assurance that the contents of the message have not been tampered with, the sender can digitally sign the message body and place the resulting digest into the header.

  • Routing information. If the message needs to be routed to many destinations, the destinations and their order can be included in the header.

  • Transactions. The recipient might have to perform some action in the scope of the sender's transaction.

  • Payment information. If the recipient of the message provides services to the client based on a per-usage fee, information necessary for collecting payment can be embedded in the header.

The Header element can be added as an immediate child element within the SOAP Envelope. The header entries appear as child nodes within the SOAP Header element. Here is an example:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   <soap:Header>     <Digest>B839D234A3F87</Digest>   </soap:Header>   <soap:Body>     <StockReport>       <Symbol>MSFT</Symbol>       <Price>74.56</Price>     </StockReport>   </soap:Body> </soap:Envelope>

The SOAP message contains a Digest element in the header that the remote application can use to ensure that the message has not been tampered with. If the client is doing a routine check to see what her stock closed at, she might not be concerned about validating the message. But if the price of the stock triggers an event within the financial software package, she might be more interested in validating the message. For example, it would be unfortunate if the financial software package were to automatically liquidate her portfolio as the result of receiving a bogus message sent by some 14-year-old kid.

mustUnderstand Attribute

Because headers are optional, the recipient of the message can choose to ignore them. However, some information that can be embedded in the header should not be ignored by the intended recipient. If the header is not understood or cannot be handled properly, the application might not function properly. Therefore, you need a way to distinguish between header information that is informative and header information that is critical.

You can specify whether the message recipient must understand an element in the header by specifying the mustUnderstand attribute with a value of 1 in the root of the header element. For example, the SOAP message might request that a remote application perform an action on the client's behalf. The following example updates a user's account information within the scope of a transaction:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   <soap:Header>     <TransactionId soap:mustUnderstand="1">123</TransactionId>   </soap:Header>   <soap:Body>     <UpdateAccountInfo>       <email>sshort@microsoft.com</email>       <firstName>Scott</firstName>       <lastName>Short</lastName>     </UpdateAccountInfo>   </soap:Body> </soap:Envelope>

The recipient of the message must update the user's account information within the scope of the client's transaction. If the transaction is aborted, the remote application must roll back the requested changes to the user's account information. Therefore, I encoded the transaction ID within the header and set the mustUnderstand attribute to 1. The remote application must either honor the transaction or not process the message.

actor Attribute

A SOAP message can be routed through many intermediaries before it reaches its final destination. For example, the previous document might be routed through an intermediary responsible for creating a transaction context. In this case, you might want to clearly specify that the TransactionId header is intended to be processed by the transaction intermediary rather than by the default actor.

The SOAP specification provides the actor attribute for annotating SOAP headers intended for certain intermediaries. The value of this attribute is the Uniform Resource Identifier (URI) of the intermediary for which the portion of the message is intended. If a header is intended to be processed by the next intermediary to receive the SOAP message, the actor attribute can be set to http://schemas.xmlsoap.org/soap/actor/next. Otherwise the actor attribute can be set to a URI that identifies a specific intermediary. Here is an example:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   <soap:Header>     <TransactionId soap:mustUnderstand="1"        actor="urn:TransactionCoordinator>123</TransactionId>   </soap:Header>   <soap:Body>     <TransferFunds>       <Source>804039836</Source>       <Destination>804039836</Destination>       <Amount>151.43</Amount>     </GetWeather>   </soap:Body> </soap:Envelope>

Because the TransactionId header element is intended for the transaction coordinator intermediary, its actor attribute is set to the intermediary's URI. The mustUnderstand attribute has also been set so that if the transaction coordinator intermediary does not understand the TransactionId header element, it must raise an error.

If the message is passed to another recipient, any header elements designated for the intermediary must be removed before the message is forwarded. The intermediary can, however, add additional header elements before forwarding the message to the next recipient. In this example, the transaction coordinator intermediary must remove the router element before forwarding it to the billing application.

One important point to note is that routing the message directly to the default actor is not considered an error. Setting the mustUnderstand attribute to 1 in combination with setting the actor attribute to urn:TransactionCoordinator does not ensure that the message will be routed through the intermediary. It means only that if the message does reach the transaction coordinator intermediary, it must comprehend the TransactionId header entry or throw an error.

In the preceding example, the intermediary needs to perform a critical task before the message is routed to the default actor. Recall that if the message does reach the transaction coordinator intermediary, it must remove the TransactionId header before forwarding the message. Therefore, the default actor can check to see whether the TransactionId header exists, which would indicate that the message was not passed through its appropriate intermediaries. However, determining whether all of the headers were processed after the message reached the default actor is not always ideal. What if the SOAP request needs to be routed through the intermediaries shown here?

The request to transfer funds must pass through a router intermediary before the funds are transferred. Suppose the router charges the customer a processing fee for forwarding the request to the appropriate banking Web service. However, before funds are deducted, the message should be routed through the transaction coordinator to initiate a transaction before any data is modified. Therefore the router intermediary and the default actor should perform all work in the scope of the transaction. Because the banking Web service is the default actor, it can check the headers to see whether the message was routed through the necessary intermediaries.

But what if the banking Web service discovers that the message was never routed through the transaction manager intermediary? If an error occurred during the funds transfer, you might not be able to undo the work performed by the router intermediary. Worse yet, the SOAP message might have been routed through the router intermediary before being routed through the transaction coordinator. If this is the case, there might be no way to tell that the procurement application performed its work outside the scope of the transaction. Unfortunately, SOAP does not provide any mechanism to ensure that the message travels through all intended intermediaries in the proper order. In the “Futures” chapter, I will discuss one of the emerging protocols for addressing this problem.

The Body Element

A valid SOAP message must have one Body element. The body contains the payload of the message. There are no restrictions on how the body can be encoded. The message can be a simple string of characters, an encoded byte array, or XML. The only requirement is that the contents cannot have any characters that would invalidate the resulting XML document.

The SOAP specification describes a method of encoding that can be used to serialize the data into the message's body. It is a good idea to conform to an established encoding scheme such as this because it allows the sender to more easily interoperate with the recipient using a well-known set of serialization rules. (I describe this encoding method later in the chapter.)

SOAP messages can generally be placed into two categories: procedure-oriented messages and document-oriented messages. Procedure-oriented messages provide two-way communication and are commonly referred to as remote procedure call (RPC) messages. The body of an RPC message contains information about the requested action from the server and any input and output parameters. Document-oriented messages generally facilitate one-way communication. Business documents such as purchase orders are examples of document-oriented messages. Let's take a closer look at each of these document types.

Two SOAP messages are paired together to facilitate an RPC method call with SOAP: the request message and the corresponding response message. Information about the targeted method along with any input parameters is passed to the server via a request message. The server then invokes some behavior on behalf of the client and returns the results and any return parameters. Most of the examples in this chapter relate to RPC method invocations, and they all follow the SOAP specification's guidelines for encoding RPC messages.

A business document such as a purchase order or an invoice can be encoded within the body of a SOAP message and routed to its intended recipient. The recipient of the document might or might not send an acknowledgment message back to the sender. (The “SOAP Encoding” section later in this chapter describes how to use serialization rules to encode the data contained within these business documents.) Because business documents often span across multiple companies, organizations such as BizTalk.org and RosettaNet serve as facilitators and repositories for schemas that define common document exchanges.

Later in the book, I will describe how to leverage the .NET platform to create and consume both RPC and document-oriented messages.

Fault Element

Everything does not always go as planned. Sometimes the server will encounter an error while processing the client's message. SOAP provides a standard way of communicating error messages back to the client.

Regardless of which encoding style was used to create the message, the SOAP specification mandates the format for error reporting. The body of the message must contain a Fault element with the following structure:

<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">   <soap:Body>     <soap:Fault>       <soap:faultcode>Client.Security</soap:faultcode>       <soap:faultstring>Access denied.</soap:faultstring>       <soap:faultactor>http://abc.com</soap:faultactor>       <soap:detail>         <MyError>           <Originator>File System</Originator>           <Resource>MySecureFile.txt</Resource>         </MyError>       </soap:detail>     </soap:Fault>   </soap:Body> </soap:Envelope>

The fault code contains a value that is used to programmatically determine the nature of the error. The SOAP specification defines a set of fault codes that you can use to describe basic SOAP errors. The fault codes are listed in Table 3-1.

Table 3-1  Base SOAP Fault Codes

Fault Code

Description

VersionMismatch

An invalid namespace for the SOAP envelope element was specified.

MustUnderstand

An immediate child element within the SOAP header containing a mustUnderstand attribute set to 1 was either not understood or not obeyed by the server.

Client

The content of the message was found to be the root cause of the error. Possible root causes of errors resulting in a Client fault code include a malformed message or incomplete information in the message.

Server

The root cause of the error was not directly attributable to the content of the message. Examples of errors resulting in a Server fault code include the server not being able to obtain the appropriate resources (such as a database connection) to process the message or a logical error during the processing of the message.

You can append more specific fault codes to the core SOAP fault codes listed in the table by using the “dot” notation and ordering the individual fault codes from least specific to most specific. For example, if the server is unable to open a database connection that is required to process the client's message, the following fault code might be generated:

<faultcode>Server.Database.Connection</faultcode>

Because the error was not the direct result of the client's message, the base fault code is Server. A more descriptive fault code is appended to the end of the base fault code. In my example, I define a category of codes for the database and a fault code specific to connection-related errors.

The faultstring element should contain a human-readable string that describes the error encountered. Here is a faultstring value for the error connecting to the database:

<faultstring>Unable to open connection to the database.</faultstring>

You can use the optional faultactor element to indicate the exact source of the error. The only exception is if an intermediary generated the error. If the error was generated at any point other than the final recipient of the SOAP message, the faultactor element must contain a URI that identifies the source of the error. Otherwise, the URI can be omitted.



Building XML Web Services for the Microsoft  .NET Platform
Building XML Web Services for the Microsoft .NET Platform
ISBN: 0735614067
EAN: 2147483647
Year: 2002
Pages: 94
Authors: Scott Short

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net