Using SOAP Headers with ASP.NET 2.0 Web Services

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.

Using SOAP Headers with ASP.NET 2.0 Web Services

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.

Copyright 2006 by WROX Publishing, Inc. All rights reserved. Reproduced here by permission of the publisher.



Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: May 7, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT This eSeminar will explore three popular games engines and how they empower developers to create exciting, graphically rich, and high-performance games for Android® on Intel® Architecture. Join us for a deep dive as experts describe the features, tools, and common challenges using Marmalade, App Game Kit, and Havok game engines, as well as a discussion of the pros and cons of each engine and how they fit into your development …

  • Instead of only managing projects organizations do need to manage value! "Doing the right things" and "doing things right" are the essential ingredients for successful software and systems delivery. Unfortunately, with distributed delivery spanning multiple disciplines, geographies and time zones, many organizations struggle with teams working in silos, broken lines of communication, lack of collaboration, inadequate traceability, and poor project visibility. This often results in organizations "doing the …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds