6

SOAP Headers

 

All the previous chapters had riveted their attention on two salient components of the SOAP packet, i.e. the Envelope and the Body. In this chapter, we shall pan our focus onto the third major component of the SOAP payload, known as the Header. The SOAP Header is optional, but when present, it should be placed immediately after the Envelope, but before the Body.

 

In the case of headers, there exists abundant flexibility to settle on what they should be used for. These headers are generally employed in order to send across information that should not to be sent as part of the SOAP body, since it does not form part of the data. Thus, specific pieces of information, such as userid, password, authentication, encryption, security information, etc. are incorporated in the headers.

 

a.asmx

<%@ WebService Language="C#" class="zzz" %>

using System;

using System.Web.Services;

using System.Web.Services.Protocols;

public class zzz

{

public yyy z;

[WebMethod]

[SoapHeader("z")]

public int abc(int i, int j)

{

return i + j;

}

}

public class yyy : SoapHeader

{

public string name;

public string pass;

}

 

a.cs

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

public class aaa : System.Web.Services.Protocols.SoapHttpClientProtocol

{

public yyy b;

public static void Main()

{

aaa a= new aaa();

a.b = new yyy();

a.b.name="vijay" ;

a.b.pass="sonal" ;

int i = a.abc(10,20);

Console.WriteLine(i);

}

[System.Web.Services.Protocols.SoapHeaderAttribute("b")]

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/abc")]

public int abc(int i, int j)

{

object[] results = this.Invoke("abc", new object[] {i,j});

return ((int)(results[0]));

}

public aaa()

{

this.Url = "http://localhost:8080/a.asmx";

}

}

public class yyy : SoapHeader

{

public string name;

public string pass;

}

 

z.bat

del *.exe

del *.dll

del zzz.cs

wsdl aa.wsdl

csc a.cs

a

 

Output

30

 

SOAP request

<?xml version="1.0" encoding="utf-8" ?>

<soap:Envelope xmlns:soap=http://schemas.xmlsoap.org/soap/envelope/ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Header>

<yyy xmlns="http://tempuri.org/">

  <name>vijay</name>

  <pass>sonal</pass>

  </yyy>

  </soap:Header>

<soap:Body>

<abc xmlns="http://tempuri.org/">

  <i>10</i>

  <j>20</j>

  </abc>

</soap:Body>

</soap:Envelope>

 

SOAP response

<?xml version="1.0" encoding="utf-8" ?>

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Body>

<abcResponse xmlns="http://tempuri.org/">

  <abcResult>30</abcResult>

  </abcResponse>

  </soap:Body>

</soap:Envelope>

 

Here, we have cogitated on the client program named a.cs, instead of the asmx file, since it is the client that dispatches the headers that are received by the asmx file. As in the past, the client program is written manually, and is followed by the wsdl program, in order to generate the proxy.

 

After the creation of a new instance of the aaa object, an object b derived from SoapHeader is created, since the intent is to send a few headers across. The class yyy, which is derived from SoapHeader, contains two members called 'name' and 'pass'. They are initialized to the values 'vijay' and 'sonal', respectively. These are the values that are required to be sent across in the header. Thus, the basic principle is to derive a class from SoapHeader, and initiate a list of variables in it, whose values are to be sent across.

 

Thereafter, the function abc is called. It is lodged in the class aaa. The only modification that is carried out to this function is, the addition of an extra attribute named SoapHeaderAttribute, containing the object b, since it holds the values that are to be sent across in the header. Everything else remains unchanged.

 

The SOAP request reaffirms the fact that the headers are actually sent across. As usual, the Envelope drifts into sight first, followed by the SOAP Header. Since the name of the class is yyy, it becomes the parent for the two child elements named 'name' and 'pass', which contain the values of 'vijay' and 'sonal', respectively. In this case, the headers are not sent back.

 

Thus, custom headers can easily be created with the help of any user-defined content. For instance, the client can send the userid and password over in the header. From the SOAP point of view, the mechanism employed by the server to handle this information is of no consequence at all.

 

Let us now expound the asmx file more comprehensively by perusing another example.

 

a.asmx

<%@ WebService Language="C#" class="zzz" %>

using System;

using System.Web.Services;

using System.Web.Services.Protocols;

public class zzz {

public yyy z;

[WebMethod]

[SoapHeader("z")]

public int abc(int i, int j)

{

int k = z.name.Length;

int l = z.pass.Length;

return k + l;

}

}

public class yyy : SoapHeader

{

public string name;

public string pass;

}

 

Output

10

 

In the asmx file, the class yyy is derived from the SoapHeader. This is akin to what was witnessed in the client program. Thereafter, an object z is created as an instance of the yyy class.

 

The function abc has been provided with an extra attribute named SoapHeader, which in very clear-cut terms, specifies that the name of the object is z, and that it contains the soap headers in the string format.

 

When the function abc gets called, the system first initializes the object z to the values found in the Header. In our case, thereafter, we merely determine the length of the two header values that are sent across, and add these up. Thus, the output obtained is 10.

However in real life, there exists ample flexibility for you to act as you desire, when faced with such a situation. Having unraveled the mystery of the client program, you would realise that the concept of web service can be comprehended with effortless ease.

 

a.asmx

<%@ WebService Language="C#" class="zzz" %>

using System;

using System.Web.Services;

using System.Web.Services.Protocols;

public class zzz

{

public yyy z;

[WebMethod]

[SoapHeader("z")]

public void abc()

{

z.name = "No";

z.pass = "yes";

}

}

public class yyy : SoapHeader

{

public string name;

public string pass;

}

 

aa.wsdl

<s:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://tempuri.org/">

  <s:element name="abc">

<s:complexType />

  </s:element>

  <s:element name="abcResponse">

<s:complexType />

  </s:element>

  <s:element name="yyy" type="s0:yyy" />

  <s:complexType name="yyy">

<s:sequence>

<s:element minOccurs="1" maxOccurs="1" name="name" nillable="true" type="s:string" />

<s:element minOccurs="1" maxOccurs="1" name="pass" nillable="true" type="s:string" />

</s:sequence>

</s:complexType>

</s:schema>

<message name="abcyyy">

<part name="yyy" element="s0:yyy" />

</message>

<operation name="abc">

<soap:operation soapAction="http://tempuri.org/abc" style="document" />

<input>

  <soap:body use="literal" />

<soap:header n1:required="true" message="s0:abcyyy" part="yyy" use="literal" xmlns:n1="http://schemas.xmlsoap.org/wsdl/" />

</input>

<output>

  <soap:body use="literal" />

</output>

</operation>

 

a.cs

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

public class aaa : System.Web.Services.Protocols.SoapHttpClientProtocol

{ 

public static void Main()

{

zzz a= new zzz();

a.yyyValue = new yyy();

a.yyyValue.name="vijay" ;

a.yyyValue.pass="sonal" ;

a.abc();

}

}

 

zzz.cs

public class zzz : System.Web.Services.Protocols.SoapHttpClientProtocol {

public yyy yyyValue;

[System.Web.Services.Protocols.SoapHeaderAttribute("yyyValue")]

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/abc")]

public void abc()

{

this.Invoke("abc", new object[0]);

}

}

[System.Xml.Serialization.XmlRootAttribute(Namespace="http://tempuri.org/", IsNullable=false)]

public class yyy : SoapHeader

{

public string name;

public string pass;

}

 

The SOAP request and the response packet remain the same as seen in the earlier program.

 

Once again, our primary focus remains impaled on the asmx file. The class yyy is derived from the class SoapHeader. Therefore, the soap headers are visible in the packet. The elements stipulated here are sent across as tags within the header. In this case, the header comprises of two elements, viz. 'name' and 'pass'. Prior to every function, we need to specify the headers that the function shall receive.

 

The function abc shall receive its headers in an object z, which is an instance of the class yyy. It has been provided as a string parameter, because the SoapHeader specifies that the object name should be in a string format. The function abc merely modifies the values contained in 'name' and 'pass'. These newly assigned values are then sent across. In the normal course, we would have accessed the values that are sent across, and then modified them. 

 

You would have discerned vast variations in the aa.wsdl file. This is because, when we encounter a class derived from SoapHeader, IIS is alerted to the fact that Headers too have been sent across.

 

The element abc has no parameters; therefore, it has an empty complexType. Since the name of the class is yyy, an element called yyy is created. The type of this element is also yyy. It does not possess any other content. Next, a complexType yyy is created to accommodate two elements called 'name' and 'pass', which correspond to the variables in the class yyy.

 

Upto this point, in the wsdl file, there has been no indication that the class yyy has been derived from the SoapHeader class. Besides, there is no allusion to the fact that yyy represents a class that is responsible for sending the Headers across. This is because the schema section looks exclusively at schemas, and Headers have no representation in a schema.

 

Now, we revert our attention right back to the message element, which was enucleated in one of the earlier chapters. Apart from the two messages sighted in the earlier programs, one more message is sent over. This message contains only a single part named yyy. The element represented by this part is called yyy. It is of type yyy, which is the class derived from SoapHeader. Thus, every class derived from SoapHeader is provided with a message element.

 

The operation abc has a simple input and output section. The input section has a Body element, comprising of a Header element from the soap namespace. The 'required' member with the value of 'true', mandates the presence of a header. The message is named as abcyyy. This name is formed by the conjunction of the name of the function, and the name of the header class. It specifies the format of the header.

 

Thus, headers are dispatched as per the functions. This statement will be proved beyond doubt in one of the forthcoming examples. The yyy part is responsible for the message, since this is where the element is expressly specified. The 'use' attribute works in a manner analogous to the 'operation' element.

 

As always, a new instance of the zzz class is created in the client program, and the yyyValue member is initialized to a yyy object. The header class is yyy. Therefore, the member that represents this header in the proxy is named as yyyValue, i.e. classname plus the word 'Value'. The 'name' and 'pass' members are initialized to 'vijay' and 'sonal', respectively. When the function abc is called, the headers are sent across, as a consequence of the attributes created in the proxy.

 

Let us now explore the proxy generated by the wsdl file. The class yyy is sighted at the absolute end of the file that has been derived from SoapHeader. This is due to the fact that 'operation' contains the Header tag for the element yyy. The function remains the same, except that the SoapHeaderAttribute has been added. The string parameter contains the name of the object that would carry the header data yyyValue. All functions that carry this attribute, send the headers across the wire.

 

So far, the SOAP Headers have been gliding in one direction only, i.e. from the client to the server. It is our intent to reverse this direction in the next example, i.e. send the Headers back from the server to the client.

 

In order to achieve this, we effect a minor modification to the asmx file, by changing the SoapHeader attribute to the following:

 

[SoapHeader("z",Direction=SoapHeaderDirection.InOut)]

 

aa.wsdl

<output>

<soap:body use="literal" />

<soap:header n1:required="true" message="s0:abcyyy" part="yyy" use="literal" xmlns:n1="http://schemas.xmlsoap.org/wsdl/" />

</output>

 

a.cs

a.abc();

Console.WriteLine(a.yyyValue.name);

Console.WriteLine(a.yyyValue.pass);

 

Output

No

Yes

 

zzz.cs

[System.Web.Services.Protocols.SoapHeaderAttribute("yyyValue", Direction=System.Web.Services.Protocols.SoapHeaderDirection.InOut)]

soap response

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Header>

<yyy xmlns="http://tempuri.org/">

  <name>No</name>

  <pass>yes</pass>

  </yyy>

  </soap:Header>

<soap:Body>

  <abcResponse xmlns="http://tempuri.org/" />

  </soap:Body>

  </soap:Envelope>

 

By merely adding one more property to the SoapHeader attribute, we have been able to add a header to the SOAP response packet. In the asmx file, the values of the 'name' and the 'pass' properties are initialized to 'no' and 'yes', respectively.

 

Further, since the Direction property has been set to 'InOut', the headers now flow in both directions. The default value of the Direction property is 'In'. The third possible value that it can assume is 'Out', which is normally used in the response packet only.

 

The wsdl file does not display the word 'InOut'. Moreover, the output element in the operation for abc, which earlier contained a single member body, now has an additional member - the header, along with its attributes.

 

Since the wsdl file contains the Header element in both the input and the output elements, the Direction property in the proxy is set to 'InOut'. The client program adds two lines at the end, to display the values of the 'pass' and 'name' members, which are modified to 'yes' and 'no', respectively.

 

a.asmx

<%@ WebService Language="C#" class="zzz" %>

using System;

using System.Web.Services;

using System.Web.Services.Protocols;

public class zzz

{

public yyy z;

public xxx z1;

[WebMethod]

[SoapHeader("z")]

[SoapHeader("z1")]

public void abc()

{

}

[WebMethod]

[SoapHeader("z1")]

public void pqr()

{

}

}

public class yyy : SoapHeader

{

public string name;

public string pass;

}

public class xxx : SoapHeader

{

public string aa;

}

 

a.cs

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

public class aaa : System.Web.Services.Protocols.SoapHttpClientProtocol

{       

public static void Main()

{

zzz a= new zzz();

a.yyyValue = new yyy();

a.xxxValue = new xxx();

a.yyyValue.name="vijay" ;

a.yyyValue.pass="sonal" ;

a.xxxValue.aa="mukhi" ;

a.abc();

a.pqr();

}

}

 

aa.wsdl

  <message name="abcyyy">

<part name="yyy" element="s0:yyy" />

  </message>

  <message name="abcxxx">

<part name="xxx" element="s0:xxx" />

  </message>

  <message name="pqrxxx">

<part name="xxx" element="s0:xxx" />

  </message>

<operation name="abc">

<soap:operation soapAction="http://tempuri.org/abc" style="document" />

<input>

  <soap:body use="literal" />

  <soap:header n1:required="true" message="s0:abcyyy" part="yyy" use="literal" xmlns:n1="http://schemas.xmlsoap.org/wsdl/" />

  <soap:header n1:required="true" message="s0:abcxxx" part="xxx" use="literal" xmlns:n1="http://schemas.xmlsoap.org/wsdl/" />

</input>

<output>

  <soap:body use="literal" />

</output>

</operation>

<operation name="pqr">

<soap:operation soapAction="http://tempuri.org/pqr" style="document" />

<input>

  <soap:body use="literal" />

<soap:header n1:required="true" message="s0:pqrxxx" part="xxx" use="literal" xmlns:n1="http://schemas.xmlsoap.org/wsdl/" />

</input>

<output>

  <soap:body use="literal" />

</output>

</operation>

 

 

zzz.cs

public class zzz : System.Web.Services.Protocols.SoapHttpClientProtocol {

public yyy yyyValue;

public xxx xxxValue;

[System.Web.Services.Protocols.SoapHeaderAttribute("yyyValue")]

[System.Web.Services.Protocols.SoapHeaderAttribute("xxxValue")]

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/abc"]

public void abc()

{

this.Invoke("abc", new object[0]);

}

[System.Web.Services.Protocols.SoapHeaderAttribute("xxxValue")]

[System.Web.Services.Protocols.SoapDocumentMethodAttribute(http://tempuri.org/pqr)]

public void pqr()

{

this.Invoke("pqr", new object[0]);

}   

}

public class yyy : SoapHeader

{

public string name;

public string pass;

}

public class xxx : SoapHeader

{

public string aa;

}

 

Soap Request 1

<soap:Header>

<xxx xmlns="http://tempuri.org/">

  <aa>mukhi</aa>

  </xxx>

<yyy xmlns="http://tempuri.org/">

  <name>vijay</name>

  <pass>sonal</pass>

  </yyy>

</soap:Header>

 

Soap Request 2

<soap:Header>

<xxx xmlns="http://tempuri.org/">

<aa>mukhi</aa>

</xxx>

</soap:Header>

 

Though the above example may span numerous pages, its sole purpose is to demonstrate the fundamental concept that, the SOAP header packets get created in accordance with the attributes placed before the function. Thus, the function that is called, determines the SOAP headers that get sent across.

 

There are two classes, xxx and yyy, both of which are derived from SoapHeader. Thereafter, two objects representing the headers, viz. z and z1, are created when the SOAP payload arrives from the client to the server. The names of these two variables are inconsequential, and do not get reflected in the wsdl file. The Attribute SoapHeader is most crucial, as this attribute determines what is to be sent across.

 

The function abc utilizes this attribute twice, and therefore, the contents of both the classes, yyy and xxx, are sent across as headers. Thus, a call to the function abc will result in the transmission of three elements in the header, viz. 'name', 'pass' and 'aa'. On the other hand, the function pqr would have only a single element in the header, i.e. 'aa', since it has only one SoapHeader attribute.

 

The wsdl file reinforces the statement that we have just made. The function abc has two headers; hence, two message elements named 'abcyyy' and 'abcxxx' are visible, unlike in the earlier program. Since the function pqr has only a single header, a single part named 'pqrxxx' is visible.

 

Every function embodies its own operation element. To begin with, the function abc in the input transmits two headers across. Hence, the header element is displayed twice. The first is a message 'abcyyy', while the second is 'abcxxx'. The output does not have any headers. Thus, no header element exists. We could possibly have combined the last two programs and sent only a single header across, using the direction property. 

The operation called pqr has a single header named xxx, which is to be sent across. The value of the direction property is 'In', by default. We have not displayed the schemas, since they fail to introduce anything innovative. The schemas that have been created for yyy and xxx are identical.

 

The client program is acquainted with the fact that there exist two objects. Hence, yyyValue and xxxValue are created, to represent the objects z and z1, respectively. Then, using the object 'a', the functions abc and pqr are called. In the proxy, we now have two classes, yyy and xxx, which are derived from the SoapHeader class.

 

Correspondingly, we also have two objects named xxxValue and yyyValue, to represent them. The proxy object is very similar to the asmx file. The function abc sends two headers across. Thus, it has two SoapHeaderAttribute attributes. The function pqr has only a single SoapHeaderAttribute.

 

Here, a round trip gets executed, in that, the asmx file gets converted to wsdl, which in turn, becomes the proxy. The first SOAP request is that of the function abc. We are sending two disparate headers from two different classes. Thus, the element 'aa' becomes a part of the xxx class, while the 'name' and 'pass' members become part of the yyy class.

 

The headers are not bundled and sent across. Instead, they are dispatched depending upon the class that they belong to. Therefore, they could enjoy the same names, and yet be differentiated at the receiving end. The second packet represents the function pqr, where only a single header is sent across. Each packet represents a single function.

 

a.asmx

<%@ WebService Language="C#" class="zzz" %>

using System;

using System.Web.Services;

using System.Web.Services.Protocols;

public class zzz {

public yyy z;

[WebMethod]

[SoapHeader("z", Required=true)]

public void abc()

{

}

}

public class yyy : SoapHeader

{

public string name;

public string pass;

}

 

aa.wsdl

<soap:header n1:required="true" message="s0:abcyyy" part="yyy" use="literal" xmlns:n1="http://schemas.xmlsoap.org/wsdl/" />

 

a.cs

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

public class aaa : System.Web.Services.Protocols.SoapHttpClientProtocol  {       

public static void Main()

{

zzz a= new zzz();

a.abc();

}

}

 

The asmx file remains unaltered, except that the function abc has the SOAP header set to the object 'z', and the Required property set to 'true'. The value of default signifies that it is mandatory for the client to send the header across, before it can send the packet over.

 

The aa.wsdl file shows only a single line, where the header element contains a 'Required' attribute. By default, it is set to a value of 'true'. In the a.cs file, since an instance of the yyyValue object is not created, an exception is thrown.

 

Output

Unhandled Exception: System.Web.Services.Protocols.SoapHeaderException: Required field/property zzz.yyyValue of SOAP header yyy was not set by the client prior to making the call.

 

a.asmx

[SoapHeader("z", Required=false)]

 

aa.wsdl

<soap:header message="s0:abcyyy" part="yyy" use="literal" />

 

zzz.cs

[System.Web.Services.Protocols.SoapHeaderAttribute("yyyValue", Required=false)]

 

soap request

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">

<soap:Body>

  <abc xmlns="http://tempuri.org/" />

  </soap:Body>

  </soap:Envelope>

 

As a result of transforming the value of the Required attribute to 'false', the header element of the wsdl file that gets generated, is by far much simpler, and contains very few attributes. It does not have any attribute named Required. The client program remains unchanged.

 

The proxy that is generated, almost mirrors the asmx file. Hence, the Required attribute is visible, along with the SoapHeader attribute, above the function abc. However, the SOAP request does not specify any Header details. Thus, in the final analysis, the value in the Required attribute determines whether a SOAP header should be sent across or not.

 

Change the code in the Main function within a.cs to the following

 

public static void Main() {

zzz a= new zzz();

a.yyyValue = new yyy();

a.abc();

}

soap request

<soap:Header>

<yyy xmlns="http://tempuri.org/">

  <name xsi:nil="true" />

  <pass xsi:nil="true" />

  </yyy>

  </soap:Header>

 

Earlier, since the yyyValue was not initialized to any object, an exception was thrown. Now, a yyy object is created, but none of the members are initialized. The SOAP packet discloses the Header details, but it uses the 'nil' attribute to signify that they are empty or null. If we change the Required attribute to 'true' in the asmx file, the SOAP request displayed above, will be generated.

 

a.asmx

<%@ WebService Language="C#" class="zzz" %>

using System;

using System.Web.Services;

using System.Web.Services.Protocols;

public class zzz

{

[WebMethod]

public string abc()

{

return "hell";

}

}

 

a.cs

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

public class aaa : System.Web.Services.Protocols.SoapHttpClientProtocol

{

public yyy b;

public static void Main()

{

aaa a= new aaa();

a.b = new yyy();

a.b.name="vijay" ;

a.b.pass="sonal" ;

string i = a.abc();

Console.WriteLine(i);

}

[System.Web.Services.Protocols.SoapHeaderAttribute("b")]

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/abc")]

public string  abc()

{

object[] results = this.Invoke("abc", new object[0]);

return ((string)(results[0]));

}

public aaa()

{

this.Url = "http://localhost:8080/a.asmx";

}

}

public class yyy : SoapHeader

{

public string name;

public string pass;

}

 

Output

hell

 

The above program, which is a C# client, is a near clone of the first C# program of the chapter. Any difference, if at all, lies in the fact that the function abc returns a string. The above program compiles by itself, without any wsdl file.

 

The salient point to be noted here is that, despite sending a header across, the asmx file seems unable to recognize or acknowledge the header's presence. Neither are there any errors generated.

 

a.asmx

<%@ WebService Language="C#" class="zzz" %>

using System;

using System.Web.Services;

using System.Web.Services.Protocols;

public class zzz

{

public SoapUnknownHeader u;

[WebMethod]

[SoapHeader("u", Required=false)]

public string abc()

{

return u.Element.InnerXml + u.Element.Name;

}

}

 

Output

<name xmlns="http://tempuri.org/">vijay</name>

<pass xmlns="http://tempuri.org/">sonal</pass>

yyy

 

In our asmx program, an object 'u' of the class SoapUnknownHeader has been freshly appended. Therefore, before the function abc gets called, this object contains all the headers that are not known. This is on account of the SoapHeader attribute having the object name of 'u'. In our case, we have only a singular header named 'yyy'.

 

The value of the Required parameter should be 'false', failing which, an unknown header would have to be sent across manually. In the function, the contents of the header are displayed, by merely employing the Element member of type XmlElement. This is achieved by displaying the InnerXml member and the 'name' property that retrieves the name of the header, i.e. yyy. 

 

A property called DidUnderstand, which accepts a boolean value, is set by the asmx file, in order to convey that it has been able to comprehend the unknown header. This is not reflected in the SOAP packet that is sent across.

 

Add the following property to a.cs

 

a.b.Actor="mukhi" ;

 

Soap request

<soap:Header>

<yyy soap:actor="mukhi" xmlns="http://tempuri.org/">

 

 

Now, we add one more property called Actor, which is part of the SoapHeader class and initialize it to mukhi. This results in the creation of an attribute named 'actor', in the SOAP request packet. The 'actor' attribute is SOAP's way of determining who should be reading and understanding the Header.

 

Here, we have assigned it a value of 'mukhi', but in practical applications, it should contain a uri.

 

This is the final illustration that carries out authentication by using an aspx file, instead of a client program.

 

a.aspx

<%@ Page Language="C#"%>

<%@ Import Namespace="Security"%>

<%@ Import Namespace="System.Web.Services.Protocols" %>

<script runat=server>

public void Page_Load()

{

s1.InnerHtml = "";

}

public void abc(Object sender, EventArgs z)

{

zzz s = new zzz();

aaa a = new aaa();

a.User = us.Value;

a.Password = pass.Value;

s.aaaValue = a;

string r = s.abc();

s1.InnerHtml = r;

}

</script>

<form runat=server>

Username:

<input type=text id=us runat=server />

<br>

Password:

<input type=text id=pass runat=server />

<br>

<input type=submit runat=server OnServerClick="abc" value="Login" />

</form>

result: <font color=red>

<span id=s1 runat=server />

 

a.asmx

<%@ WebService Language="C#" Class="zzz" %>

using System;

using System.Web.Services;

using System.Web.Services.Protocols;

public class aaa : SoapHeader

{

public string User;

public string Password;

}

public class zzz : WebService

{

public aaa z;

[WebMethod]

[SoapHeader("z")]

public string abc()

{

if (z.User == "vijay" && z.Password=="mukhi")

return "all is okay";

if (z.User == "sonal" && z.Password=="bad")

return "all is not okay";

return "somethings gone wrong";

}

}

 

a.bat

del *.dll

wsdl a.wsdl /namespace:Security

csc.exe /t:library  zzz.cs

md c:\inetpub\wwwroot\bin

copy zzz.dll c:\inetpub\wwwroot\bin\

 

SOAP request

<soap:Header>

- <aaa xmlns="http://tempuri.org/">

  <User>sonal</User>

  <Password>mukhi</Password>

  </aaa>

  </soap:Header>

- <soap:Body>

  <abc xmlns="http://tempuri.org/" />

  </soap:Body>

 

SOAP response

<abcResponse xmlns="http://tempuri.org/">

  <abcResult>something gone wrong</abcResult>

  </abcResponse>

 

We commence by creating the file a.aspx in the C:\inerpub\wwwroot folder. The language is explicitly specified as C#, because the default language is VB. This is followed by a series of unproductive Import statements, to enable you to abandon writing the namespace names over and over again. The code will eventually emanate from the Namespace Security. The whole world uses namespaces, so why should we be called the nonconformists or the odd ones out?

 

The 'runat' attribute is mandatory, although we are aware that everything runs only at the server end. The Page_Load function gets called each time the page is sent from the server to the client.

 

Thereafter, we simply initialize the s1 element, which is a span element, to a blank value. We could have used a label instead, but on a personal whim, we preferred the span element.

 

On the screen, the words 'username' and 'password' are displayed along with two textboxes, having ids of 'us' and 'pass', respectively. The user is expected to key-in a user id and a password. The 'type' for the password box should be specified as 'password' to avoid displaying the characters, since they are being entered. Moreover, there is a button that calls the function abc on the server, every time the user clicks on it.

 

Before advancing any further with deciphering the code contained in the function abc, let us examine the asmx file. The class zzz has a function named abc, which receives a soap header called 'aaa', in the form of an object 'z'. This header class aaa has two members, User and Password.

 

 

The function abc ascertains whether the User and Password contain specific pre-defined values or not. The activity performed by this function has nothing in common with the function abc of the aspx file.

 

If the username is entered as 'vijay', and the password is entered as 'mukhi', an OK message is received. However, if the user name is specified as 'sonal', and the password is specified as 'bad', an altogether different message is received. If any other values are specified, a default message is displayed. In real life, the values are compared with data contained in a database.

 

Once the asmx file is done with, create a wsdl file as before, by entering the url of http://localhost/a.asmx?WSDL in the browser. The only difference here is that the wsdl file must be saved as 'a.wsdl' in the c:\inetpub\wwwroot\bin folder. The batch file first calls the wsdl program, and the /namespace option places all the code of the class zzz in the namespace security. Thereafter, zzz.cs is compiled into a dll file, as before.

 

Once the stage is set, load the browser and enter the url as http://localhost/a.aspx. In the textboxes, enter the values for the userid and password, and then, click on the button. This calls the function abc.

 

The function abc in the aspx file first creates an object 's', which is an instance of the class zzz. The rationale behind placing the class in the bin folder is that, the system normally searches this location for classes.

 

Another object named aa is created, which is an instance of the SoapHeader class. Then, the User and Password members are initialized to the text specified in the textboxes, identified with the ids of 'us' and 'pass', respectively.

 

Once this is accomplished, the aaaValue member, which represents the header object in class zzz, is initialized to 'a', which is an instance of the SoapHeader class. In this manner, all the initialized values are passed to the header.

 

Thereafter, the function abc sends the header across, and the value returned by the remoted function is placed into the span's InnerHtml member.

 

You can change the port number to 8080 in the wsdl file, in order to observe the flow of packets in the trace program. Thus, the SOAP request and response remain the same, irrespective of whether we work with a client C# program or with an aspx file.