To read the article online, visit http://www.4GuysFromRolla.com/articles/123103-1.aspx

An Extensive Examination of Web Services: Part 6

By Scott Mitchell


Introduction


The SOAP structure... In Part 1 of this article series we looked at the pieces involved in using Web services, including SOAP, which is the standard by which the messages passed to and from a Web service are encoded by. As we discussed in Part 1, SOAP messages are comprised of the following parts:

  • A SOAP Envelope,
  • An optional SOAP Header, and
  • A SOAP Body

The diagram to the right graphically illustrates the pieces of a SOAP message and their relationship. The SOAP Envelope and SOAP Body are both required pieces of the message, and are denoted via the Envelope and Body XML elements. For example, the following shows a SOAP-formatted message that requests that the Add method be called, specifying the input parameters a and b with values 5 and 8.

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <Add xmlns="Web Service Namespace">
      <a>5</a>
      <b>8</b>
    </Add>
  </soap:Body>
</soap:Envelope>

Note that the XML placed in the Body element depends on the message itself. For request messages, the Body contains the method to call and the serialized input parameters. For the response message, the Body contains the serialized result of the method call. (For a discussion on how input and output parameters are serialized into XML, be sure to read Part 5 of this article series.)

In addition to the SOAP Body, the SOAP Envelope can also contain a Header section. This Header section can contain bits of information that are periphery to the Web method call itself. For example, a Web service method might require that those using the Web service send along a username and password, or some other means to authenticate the request. Google, which provides Web services for use by the public, requires that to use the Web service one must obtain a free license key first, and that this license key be passed to the Web service method on each invocation. A perhaps better approach would have been to remove the need for passing the license key as a parameter in the method, and rather have it passed as a header. (For more information on using Google's Web services, read: Searching Google Using the Google Web Service.)

In this article we'll examine how to configure a Web service to accept headers and how to programmatically access the contents of headers from a Web service method. We'll then see how to add a header to an outgoing Web service method call from the client!

Working with SOAP Headers


In order to have a Web service accept a SOAP header we need to perform the following three tasks:

  1. Define a SOAP header class in the Web service project
  2. Add a public member variable of the SOAP header class created in step (1) to the Web service class
  3. Add the SoapHeader attribute to the Web method(s) that need to be able to programmatically access the value of the header

Once we perform these three steps at the Web service's end, our next task is to configure the client so that it can invoke a Web service method and pass along data for the appropriate SOAP header(s). The first step, as we've discussed in earlier installments of this article series, is to create a proxy class on the client. This will auto-generate the SOAP header class defined in the Web service project and will add a public property to the proxy class through which an instance of the SOAP header can be affixed to outgoing messages.

To actually pass along a populated SOAP header when calling a Web method the client needs to first create an instance of the proxy's SOAP header class, populate its values, and then assign this header class instance to the appropriate proxy class property. Finally, calling one of the Web service's methods will invoke the method, passing along the SOAP header.

If you are a bit confused at this point, it's quite understandable. Over the next few sections we'll look at each of these steps individually and see how everything fits together.

Step 1: Creating a SOAP Header Class


For a Web service method to accept SOAP headers you must first define the structure of the headers by creating an appropriate class. SOAP headers can be very simple, such as a single string, or can be complex classes with a myriad of properties and collections. The properties of the SOAP header class are the bits of information passed in the particular header. Imagine that we wanted to pass along a SOAP header for authentication purposes. For this example imagine we plan on passing along a username and password. Our first task would be to create an appropriate class in the Web service.

The created class needs to be derived from the System.Web.Services.Protocols.SoapHeader class. The following class would constitute an appropriate SOAP header for our example, as it derives from the SoapHeader class and has two string properties: a Username and Password.

public class AuthenticationHeader : SoapHeader
{
  public string Username;
  public string Password;
}

This SOAP header class needs to be added to the Web service project. You can either add it in the .asmx file's code section, by adding it as a class after the Web service class itself, or, if you are using Visual Studio .NET, you can add a new class to the Web service project. For this latter step, right click on the project name and opt to add a new class. Name the class, AuthenticationSoapHeader.cs, or something appropriate, and then add the above code to the class.

Note that the properties in the SOAP header class that you wish to be exposed must be made public or must be created as public get/set property accessors. As we'll see shortly, when calling a Web service method and passing a SOAP header, the values of the SOAP header are serialized into XML just like the input and output parameters of a Web method. (Recall from Part 5 that only public fields or properties are serialized via XML serialization.)

Step 2: Creating an Instance of the Header Class in the Web Service Class


Once you have spelled out the class for the SOAP header, the next step is to add a public member variable to the Web service class of this SOAP header class created in the previous section. Adding a public member variable to the Web service is vital because it allows a means by which the client calling the Web method will be able to affix a SOAP header to the Web service call. The following code shows the Web service class with the added public member variable of type AuthenticationHeader:

[WebService(Namespace="http://www.4guys.com/blahblahblah")]
public class MyWebService : System.Web.Services.WebService
{
  public AuthenticationHeader AuthHeader;

  ... The Web service's methods would be defined down here ...
}

Step 3: Adding the SoapHeader Attribute to Those Web Methods That Accept the Header


In order for a Web method to be able to accept and programmatically access a SOAP header, you need to add the SoapHeader attribute before the Web method declaration. In the SoapHeader attribute simply specify the member variable name of the SOAP header chosen in step (2). For example, imagine we had a Web method called, GetMessage(), which returned a secret message if the person was authorized to see the message, else an error string is returned. This method's code might look like:

[WebMethod()]
public string GetMessage()
{
  if (user is authorized)
    return "This is the secret message!!!";
  else
    return "YOU ARE NOT AUTHORIZED TO SEE THIS MESSAGE!";
}

To allow this method to accept a SOAP header we need to add the SoapHeader attribute like so:

[WebMethod(), SoapHeader("AuthHeader")]
public string GetMessage()
{
  if (user is authorized)
    return "This is the secret message!!!";
  else
    return "YOU ARE NOT AUTHORIZED TO SEE THIS MESSAGE!";
}

Notice that the string passed into the SoapHeader attribute is the member variable name added in step (2). With this SoapHeader attribute addition, GetMessage() can directly access the properties of AuthHeader in its code. For example, if we wanted to limit access to only a single user, one with username scott and password password, we could use:

[WebMethod(), SoapHeader("AuthHeader")]
public string GetMessage()
{
  // Only allow scott/password to access this secret message...
  if (AuthHeader.Username == "scott" && AuthHeader.Password == "password")
    return "This is the secret message!!!";
  else
    return "YOU ARE NOT AUTHORIZED TO SEE THIS MESSAGE!";
}

Configuring the Client to Send Along a SOAP Header Value


At this point we have examined the necessary steps for configuring the Web service to accept headers, but we've yet to examine how the client can send a SOAP header along with its call to a particular Web method. The first step is to create a proxy class for the client. (If you have already created a proxy class, then added a SOAP header to the Web service, you'll need to update the client's proxy class. This can be done in Visual Studio .NET by right clicking on the Web Reference and choosing the Update Web Reference option.)

Creating (or updating) the proxy class will auto-generate the SOAP header class in the client's proxy. It will also add a new property to the Web service named AuthHeaderValue. In order to send a SOAP header to the Web service method, the client should do the following:

  1. Create an instance of the proxy class,
  2. Create an instance of the SOAP header class and populate its values,
  3. Affix the SOAP header to the proxy class by assigning the SOAP header class instance created in step (2) to the proxy class's AuthHeaderValue property, and, finally
  4. Call the Web service method

The following code illustrates these four steps. The GetMessage() Web method is invoked from the client passing along a username/password pair of scott and password.

// step 1, create the proxy class
localhost.MyWebService proxy = new localhost.MyWebService();

// step 2, create and populate the header class
localhost.AuthenticationHeader authInfo = new localhost.AuthenticationHeader();
authInfo.Username = "scott";
authInfo.Password = "password";

// step 3, affix the SOAP header to the proxy
proxy.AuthHeaderValue = authInfo;

// step 4, call the method
string result = proxy.GetMessage();

The value returned from GetMessage() will contain the appropriate message, based on the username/password sent via the SOAP header.

Examining the SOAP Message


The values of SOAP headers are placed in the Header element of the SOAP message. The .NET Framework uses XML serialization to serialize the header into XML. This serialized XML is then inserted into the Header element. The syntax below shows the SOAP message going from the client to the Web service when invoking the GetMessage() method with the username/password combination of scott and password.

<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
               xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
               xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <AuthHeader>
      <Username>scott</Username>
      <Password>password</Password>
    </AuthHeader>
  </soap:Header>
  <soap:Body>
    <GetMessage xmlns="Web Service Namespace" />
  </soap:Body>
</soap:Envelope>

The Web service, upon receiving this SOAP request, automatically deserializes the SOAP header XML into the member variable instance specified in the Web method's SoapHeader attribute.

Conclusion and Closing Comments


In this sixth installment of this article series we examined how to use SOAP headers to pass along information periphery to the Web method call itself. Realize that we could have just as well passed the username and password as input parameters to the Web method. However, since this information is secondary to the functioning of the method, it is a cleaner approach to use headers. As we'll see in upcoming portions of this article series, SOAP headers can be used to pass along metadata that allows for common business tasks like:

  • Encryption
  • Authentication
  • Routing
  • Transaction management
  • And many others!

One final word before signing off - realize that the technique presented in this article for passing authentication information is inherently insecure since the username and password are passed along in plain-text. That is, a nefarious hacker monitoring the network traffic could see the username and password sent along to the Web service. A better approach is to use a one-way hash function to encrypt the password. We'll see how this can be done with little work, and even less code, in a future article installment!

Happy Programming!

  • By Scott Mitchell


  • Article Information
    Article Title: ASP.NET.An Extensive Examination of Web Services, Part 6
    Article Author: Scott Mitchell
    Published Date: December 31, 2003
    Article URL: http://www.4GuysFromRolla.com/articles/123103-1.aspx


    Copyright 2017 QuinStreet Inc. All Rights Reserved.
    Legal Notices, Licensing, Permissions, Privacy Policy.
    Advertise | Newsletters | E-mail Offers