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.. T
Trang 1The 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:
Trang 2ID 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 rout ed 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:
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
Trang 3In 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
Trang 4Two 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:
Table 3-1: Base SOAP Fault Codes
Trang 5Fault 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
Using SOAP RPC Messages
One of the original design goals of SOAP was to provide an open and standard way to
facilitate RPCs using Internet technologies such as XML and HTTP In this section, I explain the method of encoding RPC-style messages described in version 1.1 of the SOAP
specification
As I stated earlier in the chapter, the SOAP specification does not dictate the way messages should be encoded, and encoding RPC-style messages is no exception Section 7 of the
SOAP 1.1 specification describes the recommended way to encode the request and
response messages The developer is free to create her own method of encoding RPC
communication In this section, however, I limit the discussion to the “standard” method of encoding RPC-style SOAP messages
Trang 6To facilitate the request/response behavior needed by RPC, you need two SOAP messages: one for the request and one for the response Here is how the request message would be
encoded for a simple C# function that adds two numbers:
public int Add(int x, int y)
{
return x + y;
}
The Add method accepts two integers as input parameters and passes the result back to the
client as a return parameter The input parameters must be packaged within the body of the request message so that they can be sent to the target application This is accomplished by packaging the parameters in a struct-like format Here is the resulting request message for
The Body element contains an Add element Each of the input parameters is represented as
a subelement within the Add element The order of the x and y elements must match the
order in which the parameters are specified in the method signature In other words, placing
y before x would be invalid Furthermore, the names and the types of the Add, x, and y
elements must be the same as the target method and its parameters I will explain data
typing in the next chapter For now, suffice it to say that the body of the request message must be in a format expected by the remote application
Now that I have created a properly formatted request message, take a look at the response generated by the remote application:
The response message returned by the remote application contains the result of the Add
method The return parameter is once again encoded in a struct-like format within the body
of the SOAP message The naming convention of the subelement within the body is the
name of the method with Result appended to it However, this naming convention is not
Trang 7dictated by the specification The first (and in this case, only) parameter contains the return
parameter of the method call As with the AddResult element, the name of the element that
contains the return parameter is not dictated by the specification
What if more than one parameter is returned to the client? Let’s take a look at a slight
variation of the Add method Add2 returns the sum of the two numbers via an output
Notice that the third parameter, sum , does not get encoded Because sum is declared as an
output parameter, there is no reason to send its initial value to the remote application Here
The response message contains the value of two parameters As I mentioned earlier, the
return parameter must always be listed first I called the element containing the return
parameter Add2Result to demonstrate that the name is not relevant The value of the sum
parameter is listed next
SOAP Encoding
Trang 8SOAP Encoding defines the way data can be serialized within a SOAP message SOAP
Encoding builds on the types defined in the XML specification, which defines a standard way
of encoding data within an XML document SOAP Encoding clarifies how data should be
encoded and covers items not explicitly covered in the XML specification, such as arrays
and how to properly encode references
Simple Types
Simple types include strings, integers, date/time, Booleans, and so on The SOAP
specification defers to the “Built-in datatypes” section of the “XML Schema Part 2:
Datatypes” specification I will talk about the XML built-in data types in the next chapter
An instance of a data type is encoded as an XML element For example, an integer called
Age would be encoded as follows:
Structures
A structure is a collection of types that serve as a template for logically grouping data For example, let’s say you need to create a function that calculates the volume of a rectangular solid Instead of passing the length, the width, and the height of the cube as separate
parameters, you can logically group the dimensional data into a RectSolid structure Then the method that calculates the volume of the solid can accept an instance of the RectSolid
structure Here is an example:
public struct Rect Solid
{
public int length;
public int width;
public int height;
First I define a structure that contains the dimensions of a solid Then I define the area A
request to calculate the volume of a rectangular solid that has a length of 2, a width of 3, and
a height of 1 can be encoded as follows:
<?xml version="1.0" encoding="utf-8"?>
Trang 9As you can see, structures map nicely to XML Each of the variables contained within the
instance of the RectSolid structure is serialized as a child element of r As you will see in the
next chapter, this follows the method of encoding structures defined in Part 1 of the XML
specification
Arrays
Another common compound data type is the array As of this writing, the XML specification does not specify how an array should be encoded The SOAP 1.1 specification fills in the
gaps Here is an example:
public int AddArray(int[] numbers)
Trang 10The array is represented by a single element within the body tag The element must contain
the soap-enc:arrayType attribute The value of the attribute describes the contents of the
array and its dimensions In the preceding example, xsi:int[3] specifies that the array
contains three integers In the next chapter, I will describe XML Schema and type definitions
in more detail
Each value in the array is listed as a subelement The names of the subelements are not
relevant, but often the names of the elements within the array will correlate with the type of data they contain
SOAP-encoded arrays can contain different elements of different types The following code returns an array containing an integer, a float, and a string:
object[] stuff = new object[3];
An array of objects called stuff is created, and then values of three different types are
assigned to each of its three elements The resulting response SOAP message is encoded
as follows:
<?xml version="1.0" encoding="utf-8"?>
Trang 11The things array is defined as type xsi:ur-type, which means that the elements can contain
data of any type In the next chapter, you will learn how to declare the type of data in each element
The final two array types I will cover are multidimensional and jagged arrays
Multidimensional arrays are rectangular by nature You can think of a jagged array as an
array contained within an array SOAP defines a method for encoding both types of arrays This example creates a multidimensional array:
// Create a block of seats 3 rows deep and 4 seats wide
string[,] seats = new string[3, 4];
A multidimensional array of labels is created, and then the array is passed to
PrintSeatLabels The resulting message is encoded as follows:
Trang 12<?xml version="1.0" encoding=" utf-8"?>
<string>row 1, seat 1</string>
<string>row 1, seat 2</string>
<string>row 1, seat 3</string>
<string>row 1, seat 4</string>
<string>row 2, seat 1</string>
<string>row 2, seat 2</string>
<string>row 2, seat 3</string>
<string>row 2, seat 4</string>
<string>row 3, seat 1</string>
<string>row 3, seat 2</string>
<string>row 3, seat 3</string>
<string>row 3, seat 4</string>
In a jagged array, which you can think of as an array of arrays, each element can contain an array of varying lengths Here is an example:
string[][] teams = new string[3][];
teams[0] = new string[3];
Trang 13teams[2] = new String[4];
The RegisterTeams function accepts a list of teams Because teams can vary in the number
of players, a two-dimensional jagged array of strings is passed to the function Each element
of the array represents a team and contains an array of player names on that team Here is how the jagged array is encoded:
Trang 14</RegisterTeams>
</soap:Body>
</soap:Envelope>
Consistent with the array encoding rules I discussed earlier, the name of the individual
elements is not important For clarity, I named each element in the teams array team and named each element in the team array player In jagged arrays, not only does the teams
element contain a soap-enc:arrayType attribute, but each of the elements within the array of teams contains a soap- enc:arrayType attribute as well
Let’s say you create an array that can hold the names of up to 1000 registrants for an
upcoming event Periodically, the list of attendees needs to be sent to various interested
parties Soon after the event has been announced, there might be only 5 people registered
If you send the list of registrants, it is not very efficient to encode all 1000 elements because only the first 5 will contain values:
// Create an array of attendees, record the first five,
// and then pass the array to RegisteredAttendees
string[] attendees[1000];
attendees[0] = "Bill Clinton";
attendees[1] = "Jimmy Carter";
attendees[2] = "Ronald Reagan";
attendees[3] = "George Bush";
attendees[4] = "Al Gore";
Trang 15As you can see in the resulting message, the soap-enc:arrayType attribute indicates that the
array contains 1000 elements even though only the first 5 were encoded If you want to
encode a portion of the array that does not start with the first element, you can specify the
starting element by using the soap- enc:offset attribute For example, if you want to encode
the next five attendees that registered for the event, the resulting message would be as
As you can see, the soap-enc:offset element specifies that the array has been offset by five
Therefore, the contents of the array contain the sixth through the tenth elements
Trang 16What if the elements to be encoded within the array are not adjacent to each other? Another means of partially encoding arrays is to use the sparse array syntax For example, say you want to create a message that contains the names of all the registered attendees that did not show up for the event The ordinal of each attendee has significance, so you are once again creating 1000 elements in an array and populating only a subset of the elements with data This time, the data will not be located in a sequential set of elements Instead, it will be
contained in elements throughout the array You can solve this problem by encoding an
array of no-shows by using the sparse array syntax Here is the resulting message:
<string soap-enc:position="[10]">Dan Quayle</string>
<string soap-enc:position="[231]">Newt Gingrich</string> <string soap-enc:position="[357]">Trent Lott</string>
<string soap-enc:position="[842]">Hillary Rodham Clinton </string>
</registrants>
</NoShows>
</soap:Body>
</soap:Envelope>
Once again, the soap-enc:arrayType attribute is used to specify that the array contains a
total of 1000 elements However, the elements that contain data are the only ones encoded within the SOAP message Because the position of the element within the array is relevant,
the soap-enc:position attribute is used to indicate where the element resides within the array
Passing Parameters by Reference
Up to this point, I have been explaining how to encode parameters that are passed by value
to a Web service But it is often necessary to pass parameters by reference For example, a client might pass information about a customer to the server so that the server can update the information on behalf of the client If the client structure were passed by value, changes made to the client’s information would not be visible to the client
Let’s take a look at how parameters that are passed by reference are encoded in a SOAP message In the first example, I create a series of Fibonacci numbers A number in a
Fibonacci series is determined by adding the two numbers directly preceding it For
example, if n1 = 1 and n2 = 1, then n3 = 1 + 1 = 2 and n4 = 1 + 2 = 3 Here is the method I
use to output a series of Fibonacci numbers:
public void FibonacciIncrement(ref int n1, ref int n2)
{
Trang 17FibonacciIncrement accepts the last two numbers and then returns the next two numbers in
the series Here are the request and response messages for the first call to
Trang 18</FibonacciIncrementResponse>
</soap:Body>
</soap:Envelope>
There is nothing surprising about the first message The two parameters are encoded as
usual What distinguishes a parameter passed by reference from one that is passed by value
is that the client needs to be notified of any changes to the value Therefore, the new values
of n1 and n2 are encoded in the response message Notice that I also follow the convention
of appending Response to the method element within the body
Another reason for passing parameters by reference is to maintain the identity of the variable being passed Consider the following example:
// Server Code:
public struct Person
{
public double Height;
public int Weight;
public int Age;
public string Hobby;
Trang 19FibonacciIncrement, the Introduce method behaves differently depending on whether the
two parameters are equal or identical (point to the same instance of Person)
The way I encode the parameters passed by reference in the Fibonacci example is not
sufficient for the Introduce method because it does not maintain the identity of the
parameters SOAP provides the id/href pattern for maintaining the identity of the parameters Here is how the call to Introduce would be encoded:
The encoded parameters do not contain any data Instead, because both parameters
reference the same instance of the Person type, the data is encoded once within the body of