Using SOAP Headers with ASP.NET 2.0 Web Services

Excerpt from Professional ASP.NET 2.0 XML

by Thiru Thangarathinam

When you communicate with a Web service using SOAP, the SOAP message that is sent to the Web service follows a standard format. The XML document inside the SOAP message is structured into two main parts: the optional headers and the mandatory body. The

Body

element comprises the data specific to the message. The optional

Header

element can contain additional information not directly related to the particular message. Each child element of the

Header

element is called a SOAP header.

SOAP headers are also a good place to put optional information, and a good means to supporting evolving interfaces. For example, imagine your bank allows you to manage multiple accounts with one ATM card. If you use your bank’s ATM, you now have to specify if you want the withdrawal to be made from my primary, secondary, or tertiary account. If you use an ATM from another bank that isn’t affiliated with you bank, you do not get asked that question. So the account identifier is clearly an optional parameter, with a reasonable default.

ASP.NET provides a mechanism for defining and processing SOAP headers. You can define a new SOAP header by deriving from the

SoapHeader

class. After you define a SOAP header, you can then associate the header with a particular endpoint within the Web service by using the

SoapHeader

attribute. Table 1 lists the properties exposed by the

SoapHeader

class.

Table 1. Properties of the SoapHeader Class

Property Description
Actor
Indicates the intended recipient of the header.
DidUnderstand
Indicates whether a header whose

mustUnderstand

attribute is

true

was understood and processed by the recipient.

EncodedMustUnderstand
Indicates whether a header whose

mustUnderstand

attribute is

true

and whose value is encoded was understood and processed by the recipient. This property is used when communicating with the SOAP 1.1 version.

EncodedMustUnderstand12
Very similar to

EncodedMustUnderstand

except that it is used in conjunction with SOAP 1.2 version.

MustUnderstand
Indicates whether the header must be understood and processed by the recipient.
Relay
Indicates if the SOAP header is to be relayed to the next SOAP node if the current node does not understand the header.
Role
Indicates the recipient of the SOAP header.

By default, the name of the class derived from

SoapHeader

will become the name of the root header element, and any public fields or properties exposed by the class will define elements within the header. SOAP headers are defined by classes derived from the

SoapHeader

class. Elements within the header are defined by public fields or read/writable properties.

Implementing a SOAP Header Class

Imagine you want to modify the QuotesService used in the previous examples to accept a SOAP header that contains the payment information. Listing 1 illustrates the definition of the Payment header.

Listing 1: SOAP Header Declaration

using System;
using System.Xml;
using System.Xml.Serialization;
using System.Web.Services.Protocols;
public class SoapPaymentHeader : SoapHeader
{
  private string nameOnCard;
  private string creditCardNumber;
  private CardType creditCardType;
  private DateTime expirationDate;	
  public string NameOnCard
  {
    get { return nameOnCard; }
    set { nameOnCard = value; }
  }
  public string CreditCardNumber
  {
    get { return creditCardNumber; }
    set { creditCardNumber = value; }
  }
  public CardType CreditCardType
  {
    get { return creditCardType; }
    set { creditCardType = value; }
  }
  public DateTime ExpirationDate
  {
    get { return expirationDate; }
    set { expirationDate = value; }
  }	
}

The preceding class definition defines a SOAP header named

SoapPaymentHeader

with four child elements:

NameOnCard

,

CreditCardNumber

,

CreditCardType

, and

ExpirationDate

.

This code uses an enum named

CardType

that is declared as follows:

public enum CardType
{
  VISA,
  MASTERCARD,
  AMX,
  DISCOVER
}

After you define the headers, the next step is to associate them with the actual Web service method.

Processing the SOAP Header from a Web Service

The SoapHeader attribute is used to associate a SOAP header with a Web method. A public member variable is added to the

WebService

class to hold an instance of the class derived from the

SoapHeader

class. The name of the member variable is then communicated to the ASP.NET runtime via the SoapHeader attribute. Listing 2 shows the modified

QuotesService

class definition that is now capable of processing the

SoapPaymentHeader

.

Listing 2: Web Service Method That Processes the SOAP Header

using System;
using System.Xml;
using System.Web;
using System.Collections;
using System.Web.Services;
using System.Web.Services.Protocols;
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
public class QuotesService : System.Web.Services.WebService
{
  public SoapPaymentHeader paymentHeader;
  [WebMethod(Description = 
  	"Returns real time quote for a given stock ticker")]
  [SoapHeader("paymentHeader", Direction = SoapHeaderDirection.In)]
  public double GetStockPriceWithPayment(string symbol)
  {
    //Process the SOAP header
    if (paymentHeader != null)
    {
      string nameOnCard = paymentHeader.NameOnCard;
      string creditCardNumber = paymentHeader.CreditCardNumber;
      CardType type = paymentHeader.CreditCardType;
      DateTime ExpirationDate = paymentHeader.ExpirationDate;
      //Process the payment details
      //.........
      ///End Processing
    }
    else 
      throw new SoapHeaderException("Invalid information in SOAP Header", 
        SoapException.ClientFaultCode);
    double price;
    switch (symbol.ToUpper())
    {
      case "INTC":
        price = 70.75;
        break;
      case "MSFT":
        price = 50;
        break;
      case "DELL":
        price = 42.25;
        break;
      default:
        throw new SoapException("Invalid Symbol", 
			SoapException.ClientFaultCode,
          "http://wrox.com/quotes/GetStockPriceWithPayment");
    }
    return price;
  }
}

Listing 2 declares a member variable named

paymentHeader

to hold the data contained in the payment SOAP header.

public SoapPaymentHeader paymentHeader;

Note that you do not create an instance of the

SoapPaymentHeader

class because the ASP.NET runtime is responsible for creating this object and populating its properties with the data contained within the payment header received from the client.

Next you add two

SoapHeader

attributes to declare that the headers should formally be described as part of the Web method. The constructor of the

SoapHeader

attribute takes a string that contains the name of the public member variable that should be associated with the SOAP header.

[SoapHeader("paymentHeader", Direction = SoapHeaderDirection.In)]

You set the

Direction

property to

SoapHeaderDirection.In

. The

Direction

property indicates whether the client or the server is supposed to send the header. In this case, because the payment header is received from the client, you set the

Direction

property to

SoapHeaderDirection.In

. If a SOAP header is received from the client and then also sent back to the client, the value of the

Direction

property should be set to

SoapHeaderDirection.InOut

.

Next the code processes the contents of the SOAP payment header. If the payment header information is not passed in, you throw a

SoapHeaderException

back to the callers.

throw new SoapHeaderException("Invalid information in SOAP Header", 
  SoapException.ClientFaultCode);

The rest of the implementation is similar to the previous example.

Now that you have associated the payment header with the Web method, the next task is to send the payment SOAP header from the client. Listing 3 shows the ASP.NET pages that creates an instance of the SOAP payment header and sends that as part of the SOAP request that is sent to the server.

Listing 3: Passing SOAP Header Information to a Web Service Method

<%@ Page Language="C#" %>
<%@ Import Namespace="System.Web.Services.Protocols" %>
<script runat="server">
  void btnGetQuote_Click(object sender, EventArgs e)
  {
    try
    {
      QuotesProxy.SoapPaymentHeader header = new 
        QuotesProxy.SoapPaymentHeader();
      header.CreditCardNumber = "xxxxxxxxxxxxxxxx";
      header.CreditCardType = QuotesProxy.CardType.VISA;
      header.NameOnCard = "XXXXXX";
      header.ExpirationDate = DateTime.Today.AddDays(365);
      QuotesProxy.QuotesService obj = new QuotesProxy.QuotesService();
      obj.SoapPaymentHeaderValue = header;
      output.Text = 
	  	obj.GetStockPriceWithPayment(txtSymbol.Text).ToString();
    }
    catch (SoapHeaderException soapEx)
    {            
      output.Text = "Actor : " + soapEx.Actor + "<br><br>";
      output.Text += "Code  : " + soapEx.Code + "<br><br>";
      output.Text += "Message: " + soapEx.Message + "<br><br>";
      output.Text += "Detail: 
	  	" + Server.HtmlEncode(soapEx.Detail.OuterXml);
    }
    catch (Exception ex)
    {
      output.Text = "Exception is : " + ex.Message;
    }
  }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
<head id="Head1" runat="server">
  <title>Passing SOAP Headers to a Web Service Method</title>
</head>
<body>
  <form id="form1" runat="server">
    <div>
      Enter Stock Symbol: <asp:TextBox runat="server" ID="txtSymbol" />
      <asp:Button Text="Get Quote" runat="server" ID="btnGetQuote" 
        OnClick="btnGetQuote_Click" />
      <br /><br /><br />
      <asp:Label Font-Bold=true runat="server" ID="output" />
    </div>
  </form>
</body>
</html>

To start with, Listing 3 creates an instance of the

SoapPaymentHeader

object and sets its properties to appropriate values. It then sets the

SoapPaymentHeaderValue

property of the proxy object to the

SoapHeader

object.

obj.SoapPaymentHeaderValue = header;

After you set the

SoapPaymentHeaderValue

property, the contents of the SOAP header will be automatically transferred as part of the SOAP message. Next you also handle any errors thrown by the Web service using two catch blocks:

SoapHeaderException

block and a generic

Exception

block.

Conclusion

As you have seen from this article, the optional SOAP

Header

element can contain additional information not directly related to the particular message. In addition to the optional data related to the message, the SOAP header can also contain information processed by infrastructure within a Web server.

This article is adapted from Professional ASP.NET 2.0 XML by Thiru Thangarathinam (Wrox, 2006, ISBN: 0-7645-9677-2), from Chapter 13 "XML Web Services." Thiru works for Intel Corporation where he focuses on enterprise applications and service oriented architecture. He is a Microsoft Certified Application Developer (MCAD) specializing in architecting and building distributed n-tier applications using ASP.NET, C#, VB, ADO.NET, and SQL Server.Thiru’s other recent article on Wrox.com is "Atlas Foundations: ASP.NET 2.0 Callback" excerpted from chapter 9 "XML Data Display" in Professional ASP.NET 2.0 XML.

Tags:

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *