Mark Wilson I am the creator of TopXML. I am available for international and local (Australia) contracts. I am a Solution Architect/Business Analyst. I have worked in IT in several countries (NZ, Australia, South Africa, UK) building and training teams for government and very large non-governmental organizations. I am ex-Microsoft Consulting Services. I wrote the first book on Microsoft XML published in 2000 called XML Programming with VB and ASP. Most recently I have been building tools for the SEO industry. Ask me for a 37 point SEO health-checkup for your website.
First posted :
03/24/2008
Times viewed :
464
SOAP Headers
Besides the data embedded inside the <Body> tag, SOAP messages can also contain
information to describe the context and the intent of the message. This
information is placed in header entries embedded inside a <Header> tag that is placed before the <Body>. Headers are not necessarily intended for
the same processor as the <Body>,
they can be intended for processors along the path to the ultimate destination,
for example to influence the routing of the massage.
We can leverage
header entries for all sorts of purposes in our applications. We can store user
information to authenticate the message at the destination; we can add a
session identifier if we are going to transmit multiple messages that belong
together or we can make sure that messages are logged along the way before they
reach their final destination.
We add headers to a
SOAP message simply by passing an array of header objects to the Serialize() method of the SoapFormatter.
The class for these header objects is found in the System.Runtime.Remoting.Messaging namespace and is
appropriately named Header. The Header class exposes several fields to set the name of
the header entry, its value and so on. We can initialize the fields of a Header object one-by-one or all at once through one of
the class constructors shown in table 13.6
Table The
constructors of the Header class initialize the Header’s fields.
Constructor
Description
public
Header(string name, object value);
Instantiates a
required Header object with a default namespace.
public
Header(string name, object value,
bool mustUnderstand);
Instantiates a
Header object with a default namespace. The mustUnderstand parameter
indicates whether or not processing this header entry is required.
public Header(string
name, object value,
bool mustUnderstand,
string headerNamespace);
Instantiates a
Header object. The mustUnderstand parameter indicates whether or not
processing this header entry is required. The headerNamespace explicitly
specifies the namespace for this header entry.
NOTE: The SOAP
protocol states that Header entries
can be intended for entities along the message’s path instead of the ultimate
destination of the message. These applications are called actors and a header
entry identifies its target actor by an actor attribute. The Header class does not support
the actor concept at all. As a result the SoapFormatter
can only generate headers intended for the same processing entity as the body
of the message, the “ultimate destination”.
We can extend our
example sender class to include a unique transaction ID in each message. In
this version of the example our (imaginary) system provides a transaction ID to
the sender’s SendObject()
method which we package up in a Header object
and then pass to the Serialize(). The
following listing shows the modified SoapObjectSender
class.
Listing The sender class with modifcations to include
message headers
using System;
using System.IO; // for Stream
using System.Messaging; // for Message and MessageQueue
using System.Runtime.Remoting.Messaging; // for Header |#1
using System.Runtime.Serialization.Formatters.Soap;
public class SoapObjectSender
{
private MessageQueue
_DestinationQueue;
private SoapFormatter
_Formatter = new SoapFormatter();
public
SoapObjectSender(string destinationQueueName ){
_DestinationQueue =
new MessageQueue(destinationQueueName);
_Formatter = new
SoapFormatter();
}
public void SendObject(
object o, int transactionId ){
Message msg = new
Message();
_Formatter.Serialize(
msg.BodyStream, o, |#2
new Header[] { new Header("TransactionId", |
transactionId) } ); |
_DestinationQueue.Send( msg );
Console.WriteLine( "Message
sent." );
}
}
(annotation) <#1 The Header class is located in this
namespace.>
(annotation) <#2 Serialize() accepts a Header array
containing the to headers to include in the message.>
This tiny modification to the original class is all that is
necessary to produce a SOAP message with headers. The SoapFormatter inserts the headers and
formats them compliant with section 4.1 of the SOAP standard as we can see in
the next SOAP message fragment.
The content of a header entry is not limited to value types.
We can also encode complex types in the header information. Once again, these
have to have permission to be serialized from their developer, i.e. they need
to be marked with the SerializableAttribute.
A SOAP message fragment showing the header resulting from
a SendObject() call
<SOAP-ENV:Envelope … >
<SOAP-ENV:Header>
<h3:TransactionId
xsi:type="xsd:int"
SOAP-ENV:mustUnderstand="1"
xmlns:h3="http://schemas.microsoft.com/clr/soap"
SOAP-ENC:root="1">12345</h3:TransactionId>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
…
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
The TransactionIdHeader
object results in a <TransactionId>
element with the value stored in the Header object. Upon closer
examination of the element we find attributes on the TransactionId element that we didn’t
explicitly specify. Let’s take a closer look before we move on to see how we
can read headers from a received message.
The first of these attributes is the xsi:type attribute to describe the
type contained in the header element. The standard requires this attribute for
elements where the type cannot be inferred from a schema. The next attribute is
the SOAP-ENV:mustUnderstand
attribute defined to indicate that the recipient must process or this header or
discard the message. The possible values for this attribute are 0, meaning the
header is optional and 1 meaning that the recipient has to understand the
message or discard it. The Header
class chooses 1 as the default value for this attribute because we did not
explicitly set the value when we instantiated the object.
NOTE: The Header
class’ default value of 1 for the SOAP-ENV:mustUnderstand attribute is the opposite of the
standard’s default value of 0.
Since we did not provide a namespace for our TransactionId
header, but the standard requires one for all header entries, the SoapFormatter
added a default namespace declaration to the element. The last attribute on the
header entry is the optional SOAP-ENC:root attribute providing a hint that this
element contains a serialized object graph.