11

ASP.Net WebServices

 

The very purpose of this chapter is to introduce the use of complex regular expressions, to specify the content that should be selected in an HTML file. People have invested millions of dollars in building the html infrastructure. Hence, it cannot be simply wished away.

 

An html file could contain stock prices, which needs to be extracted with minimal effort.  Besides, there are web sites like Barnes and Noble or Amazon, which run a bookshop online.

 

These servers contains a script written in any scripting language, which accepts a parameter, such as- an ISBN number, and retrieves a book that tallies with this number, from its database. Thus, it is essential to command the ability to pass parameters to a program, which is running on the web server. The custom extensions are one of the various ways of using the already existent infrastructure.

 

This modus operandi is termed as screen or html scrapping, wherein, the contents of an html file are accessed, and the values are extracted as per our bidding.

 

A Web site can now be converted into a Web Service by employing the extant logic and data. Thus, the knowledge of ASP.NET is put to effective use, thereby, evading the compulsion to dabble around with protocols of HTTP.

 

a.aspx

<html>

<script language="C#" runat="server">

public void Page_Load(Object o, EventArgs E)

{

try

{

zzz m= new zzz();

abc1Matches n = m.abc1();

a1.InnerHtml = n.aa;

a2.InnerHtml = n.cc;

}

catch (Exception e)

{

a3.InnerHtml = "Error " + e.ToString();

}

}

</script>

<form runat="server">

<span id="a1" runat="server"/>

<p>

<span id="a2" runat="server"/>

<p>

<span id="a3" runat="server"/>

</form>

</body>

</html>

 

aa.wsdl

<?xml version="1.0"?>

<definitions xmlns:s="http://www.w3.org/1999/XMLSchema" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:s0="http://tempuri.org/" targetNamespace="http://tempuri.org/" xmlns="http://schemas.xmlsoap.org/wsdl/">

<types>

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

<s:element name="s1" type="s:string" nullable="true"/>

</s:schema>

</types>

<message name="i1"/>

<message name="o1">

<part name="Body" element="s0:s1"/>

</message>

<portType name="p1">

<operation name="abc1">

<input message="s0:i1"/>

<output message="s0:o1"/>

</operation>

</portType>

<binding name="b1" type="s0:p1">

<http:binding verb="GET"/>

<operation name="abc1">

<http:operation location="/a.html"/>

<input>

<http:urlEncoded/>

</input>

<output>

<text xmlns="http://microsoft.com/wsdl/mime/textMatching/">

<match name='aa' pattern='bb&gt;(.*?)&lt;'/>

<match name='cc' pattern='dd&gt;(.*?)&lt;'/>

</text>

</output>

</operation>

</binding>

<service name="zzz">

<port binding="s0:b1">

<http:address location="http://localhost" />

</port>

</service>

</definitions>

 

zzz.cs

using System.Xml.Serialization;

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

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

public zzz()

{

this.Url = "http://localhost";

}

[System.Web.Services.Protocols.HttpMethodAttribute (typeof(System.Web.Services.Protocols.TextReturnReader), typeof(System.Web.Services.Protocols.UrlParameterWriter))]

public abc1Matches abc1() {

return ((abc1Matches)(this.Invoke("abc1", (this.Url + "/a.html"), new object[0])));

}

}

public class abc1Matches

{

[System.Web.Services.Protocols.MatchAttribute ("bb>(.*?)<")]

public string aa;

[System.Web.Services.Protocols.MatchAttribute("dd>(.*?)<")]

public string cc;

}

 

a.html

<HTML>

<HEAD>

<bb>vijay</bb>

</HEAD>

<BODY>

<dd>Mukhi</dd>

</BODY>

</HTML>

 

a.bat

del *.dll

del zzz.cs

wsdl aa.wsdl

csc /t:library zzz.cs

 

Output

Vijay

Mukhi

 

When the aspx file is loaded in the browser, the code specified in the Page_Load function is called first. Here, we first create an object 'm', as an instance of class zzz. This class is present in the dll, which gets created in the bin folder. Once the object has been created, we call a function named abc1, from this class. The return value of this function, which is an abc1Matches object, is stored in the object 'n'.

 

The abc1Matches class is present in the proxy dll. It contains two members named aa and cc, both of type string. The function abc1 first initializes the two members, and thereafter, returns this object. The values contained in them are then displayed, using the two spans, viz.a1 and a2. If an error occurs, the text of the error is displayed, using the span a3.

 

If you attempt to load the aspx file without creating the dll, an error would be generated. To create these two classes in the dll, the presence of a WSDL file is inescapable. So, we create the wsdl file manually, and then, run the WSDL program on it. Once the .cs file is created, the library is formed, by employing the services of the csharp compiler.

 

As usual, the wsdl file has the root tag definitions embedded in it, containing countless namespace prefixes. Then, we encounter the type element, which simply defines an element s1. Its type belongs to the string type in the XML schema world. Since this is our second rendezvous with the wsdl file, we will elude delving into the minute details, for instance- the types referring to a schema document, et al. Next, we find two messages:

     i1 - It is an empty message.

     o1 - It has one part called Body, which has the likeness to an element of a string type.

 

Messages use the portType element. Notwithstanding the fact that the messages are blank, the element must be specified. The part name is called Body, although any other name would also have sufficed, since it is not referred to anywhere in the program. This message contains a string text. Had we so desired, we could have completely omitted the types section; and instead, we could have inserted the part element with the following:

 

<part type="s:string"/>

 

However, this is an allusive way or a convoluted way of stating the same thing. Messages are the foot soldiers for the portType, and they define an operation. Thus, the message element called p1 defines an operation abc1, which is to be performed.

 

An operation expects an input and an output type. The messages assign the contents to these types. The message i1 assigns a value of 'null' to the input type, whereas, the message o1 assigns a string to the output type. The binding b1 accepts the details from a portType, and it conjoins the verb GET to it. The verb cannot have arbitrary names like 'vijay', POST, PUT, etc.

 

Assigning an incorrect value to the verb will bring the WSDL to a grinding halt. As a consequence of this, the zzz.cs file that gets generated, would be erroneous.

 

The operation element specifies the name of the html file that we want to work with. The / symbol is of utmost significance, since it indicates that, the file will be found in the root of the web server's directory, i.e. at c:\inetpub\wwwroot. The input is specified as http:urlencoded. As a result of this, all values that are sent across, shall be URL encoded. It is the http specification, which finally determines the url.

 

Thus, if we have two textboxes named a1 and a2, containing 'vijay' and 'mukhi' respectively, the URL shall contain the following: a1=vijay&a2=mukhi.

 

The vital component of the above exercise is the output element. The text element specifies the default namespace for the elements that follow, and it is well nigh impossible to mutate these URIs. They are hard-wired into the WSDL program. However, when we go to the above URI's, we do not chance upon anything of interest. In this case, the URI informs us that there is no valid page at the Microsoft site.

 

Next, we come across the two match elements, named aa and cc. These are the two elements that are referred to, in the aspx file. We will elucidate the pattern in due course.

 

Finally, there is a service called zzz, which is the name of our class. It is followed by the port bindings, which specify the newly created binding known as b1. The address of a.html is also present on the server, i.e. localhost. When we run the WSDL program on this wsdl file, it will result in the creation of a file called zzz.cs, embodying a class called zzz.

 

The above WSDL file is grossly deviant from what we have utilized so far, since SOAP is conspicuous by its absence right now. The class zzz is derived from the class HttpGetClientProtocol. The Url property does not specify either the asmx file or any other file; it only contains the machine name.

 

In the proxy, the attribute of HttpMethodAttribute that has been specified, has never been sighted so far, since the client had been using SOAP. This attribute is pressed into action only when clients begin to use the HTTP-GET or HTTP-POST methods. This attribute is responsible for determining the following:

     The serialization that would occur, for parameters that are sent across the wire to a WebService.

     The manner in which the result that has been returned, would be received.

 

Most of these attribute classes cannot be inherited, since they are sealed classes. The first parameter to the constructor is a Type, which embodies a class that is used to deserialize the response from the web service.

 

There is no help or documentation available for the class TextReturnReader. Hence, we are not in a position to elaborate on it. The second parameter in the attribute is the UrlParameterWriter class, which serializes the parameters for the web service. In this case too, we feel stranded, since the help pages for this class appear to be either stolen, or misplaced. There could also be a distant possibility that Microsoft never got around to fabricating them.

 

The attribute applies to the function abc1, which corresponds with the name of the operation. The return type consists of the name of the function 'abc1', with the word 'Matches' appended to it. The return value, i.e. class abc1Matches, contains two string variables, viz. aa and cc. This is because, there exist two match elements with identical names.

 

The fucntion abc1 then calls the familiar Invoke function, with the first parameter as abc1. The second parameter is a string, which finally crystallizes into http://localhost/a.html. The third parameter indicates that no parameters exist.

 

When abc is called, it returns an object, which looks like 'abc1Matches'. The two spans are initialized, with the help of these two variables. In the class abc1Matches, each of the variables contains an attribute of MatchAttribute. As is evident from the name, the attributes that are to be matched, have been specified. The singular parameter to the constructor is a regular expression, which locates a string in the html file, a.html.

 

The html file contains two tags named bb and dd. The two variables in the abc1Matches object, i.e. aa and cc, are assigned the values 'vijay' and 'Mukhi', respectively.  This happens since these words are enclosed in the tags 'bb' and 'dd', correspondingly. Finally, these variables are displayed in the span elements.

 

Thus, the contents placed between the tags in an html file are picked up, merely by creating a WSDL file with a match element.

 

GET Request

GET /a.html HTTP/1.1

User-Agent Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 1.0.2914.16)

Connection: Keep-Alive

Host:localhost

 

GET Response

HTTP/1.1 200 OK

Server: Microsoft-IIS/5.0

Date: Fri, 19 Oct 20 01 02:54:07 GMT

Content-Type: text/html

Accept-Ranges: bytes

Last-Modified: Wed, 17 Oct 2001 09:44:20 GMT

ETag: "0f2624bf056c11:9ca"

Content-Length: 83

<HTML>

<HEAD>

<bb>vijay</bb>

</HEAD>

<BODY>.

<dd>Mukhi</dd>.

</BODY>

</HTML>

 

aa.wsdl

<http:address location="http://localhost:8080" />

 

In order to ameliorate our understanding of the above example, we effect a minute alteration to the WSDL file. This is done by way of modifying the port number to 8080 in the address element; subsequent to which, we run the file a.bat. The SOAP trace program is set to 'unformatted trace', since we are interested in deciphering the HTTP headers, which are transported to and fro.

 

The formatted option only gives the SOAP payload, which does not get generated in the above case. The trace, very clearly and cogently reveals that the Get request asks for the a.html file from the server. The web server concedes to this request, and in doing so, returns the entire HTML file. The SOAP data is nowhere in sight at the moment!

 

When the Invoke function scrutinizes the parameters, it awakens to the fact that, this function is distinct from the Invoke function that used to get called earlier. It is privy to the fact that it has to pick up the html file using the HTTP protocol, and that, an object has to be returned in another object, which is an instance of abc1Matches.

 

Now, in order to assign values to the two variables 'aa' and 'cc', the pattern is matched in the html file. The variable 'aa' contains a pattern that begins with the name of tag 'bb'. This tag is followed by the > and < symbols, which enclose a pattern that is in accordance with the rules governing a regular expression. We will not sermonize on the topic of regular expressions. We shall only divulge the following:

     The . symbol matches a single character.

     The * symbol matches one or more characters.

     If we remove the quantifier?, all characters will be matched, because the ? symbol chooses the least match.

           

In the WSDL file, the pattern attribute is written as bb&gt;(.*?)&lt;.

 

We commence with the tag name 'bb'. Since we cannot insert the < and > symbols in an XML file, as these are reserved symbols, we use the more elaborate form, i.e. &gt;. The open and close symbols are part and parcel of the syntax. An error will be generated if they are dispensed with. The actual pattern eventually matches the smallest content in the tag 'bb'. The value 'vijay' is then supplied to the variable aa.

 

The pattern matching is fashioned by the Invoke function, after it receives the html file. In this manner, we can access the content of any tag in an html file, with the least amount of effort.

 

a.html

<bb>vijay is good</bb>

<bb>vijay1</bb>

<ddd>Mukhi1</ddd>

<dd>Mukhi</dd>

 

Output

vijay is good

Mukhi1

 

The a.html file is now modified to encompass only the required tags. All the other tags, like Body, can be eradicated with effortless ease.

 

The file now contains two occurrences of the tag bb. However, the output displays only the first occurrence of the tag. Resorting to the same analogy, the second tag name has been modified from dd to ddd. Furthermore, treading close on the heels of ddd, there subsists another tag named dd. However, the content of ddd is displayed, since it works on the basis of 'first come first served'.

 

a.aspx

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

<%@ Import Namespace="System.Net" %>

<Script runat="server">

public void abc(Object s, EventArgs e)

{

zzz a = new zzz();

abc1Matches m;

m = a.abc1(t1.Text,"Bye");

l1.Text = m.aa;

}

</Script>

<asp:Label id=l1 runat="server" />

</font></P><FONT face=arial><B>

<P>

<form runat="server">

<asp:TextBox id=t1 runat="server" /><P>

<asp:Button runat="server" Text="Click" OnClick="abc" />

</form>

 

a.html

<HTML>

<TITLE>Vijay.com:name:XML .NET</TITLE>

</html>

 

aa.wsdl

<message name="i1">

<part name="mukhi" type="s:string"/>

<part name="sonal" type="s:string"/>

</message>

 

<text xmlns="http://microsoft.com/wsdl/mime/textMatching/">

<match name="aa" pattern="TITLE&gt;vijay.com.name.(.*?)&lt;" ignoreCase="true"/>

</text>

 

zzz.cs

public abc1Matches abc1(string mukhi, string sonal)

{

return ((abc1Matches)(this.Invoke("abc1", (this.Url + "/a.html"), new object[] {mukhi, sonal})));

}

public class abc1Matches {

[System.Web.Services.Protocols.MatchAttribute("TITLE>vijay.com.name.(.*?)<", IgnoreCase=true)]

public string aa;

}

 

SOAP request

GET /a.html?mukhi=13&sonal=Bye HTTP 1.1

 

Output

XML.Net

 

In the above aspx file, the function abc creates the zzz object as before, with one exception, i.e. the abc1 function accepts two parameters, both of which are strings. Therefore, in the wsdl file, we must incorporate a mechanism of specifying parameters to the function. To facilitate this, we carry out the requisite revisions to the WSDL file.

 

In the earlier program, the message named i1 was devoid of any content or parts. But now, we inset two parts in the message, viz. 'mukhi' and 'sonal'. In a GET method, these parts merely comport themselves as parameters to the operation, which consumes this part. The operation abc1 then simply creates a function abc1, which accepts two string parameters, 'vijay' and 'mukhi'.

 

The second change incorporated in the wsdl file relates to the match element. Here, the pattern attribute is specified in the form of a Regular Expression, which would be employed to extract data. We intend to extract data contained in the tag TITLE, which has static or boilerplate text in the following format:

     the word 'vijay',

     followed by any single character,

     followed by the word 'com',

     followed by any character,

     followed by the word 'name',

     and finally, followed by any character.

 

Thus, the resultant pattern that is specified is as follows:

 

            TITLE&gt;vijay.com.name.(.*?)&lt

Thus, the variable aa would contain the text that follows the pattern, upto the end of the tag. There are books aplenty on the topic of expressions. Therefore, we have decided to refrain from developing it any further.

 

Since the extra attribute ignoreCase is 'true', the casing will be ignored completely. Thus, it is of no consequence whether we specify 'vijay' in capitals or in small letters in the html file.

 

In the zzz.cs file, the function abc1 contains two string parameters, viz. 'mukhi' and 'sonal'. Further, the abc1Matches class copies not only the regular expression, but also the attributes. The SOAP request simply appends the values of the variables 'mukhi' and 'vijay' to the URL, as though they were names of html elements. The variables are separated by the & symbol, in accordance with the rules of URL encoding.

 

b.aspx

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

<Script runat="server">

public void Page_Load(Object s, EventArgs e)

{

if ( Request["mukhi"] == "13" )

Response.Write("<HTML><TITLE>Vijay.com:name:XML .NET1</TITLE></HTML>");

else

Response.Write("<HTML><TITLE>Vijay.com:name:XML .NET2</TITLE></HTML>");

}

</Script>

 

aa.wsdl

<http:binding verb="GET"/>

<operation name="abc1">

<http:operation location="/b.aspx"/>

<input>

 

We have made a singular change to our aa.wsdl file. The name of the file, or location in the operation element, has been changed from a.html to b.aspx. This location could be any one of these- an aspx file or a pl (PERL) program, or any other script file.

The file b.aspx will now send across a different TITLE content, depending upon the contents of the parameter named 'mukhi'.

 

Now, if we run the program by typing 13 into the textbox, the file b.aspx, will be called. It is for the first time that the Request, with the name of the parameter 'mukhi' enclosed within square brackets [ ], representing the indexer, will hold a value of 13.

 

Hence, the 'if' statement will transmit the HTML file containing NET1. In the second round, the result will contain NET2, which would then be sent across.

 

a.aspx

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

<%@ Import Namespace="mmm"%>

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

<script runat=server>

public void Page_Load() {

s1.InnerHtml = "";

s2.InnerHtml = "";

}

public void abc(Object sender, EventArgs e)

{

sss s = new sss();

aaa a = new aaa();

a.User = u.Value;

a.Password = p.Value;

s.a1 = a;

try

{

string r = s.abc();

s1.InnerHtml = r;

}

catch (SoapException so)

{

s2.InnerHtml = so.Message;

}

}

</script>

<form runat=server>

Username:

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

<BR>

Password:

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

<P>

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

</form>

<span id=s1 runat=server />

<span id=s2 runat=server />

 

aa.wsdl

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

<definitions xmlns:s="http://www.w3.org/2001/XMLSchema" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s0="http://tempuri.org/" targetNamespace="http://tempuri.org/" xmlns="http://schemas.xmlsoap.org/wsdl/">

<types>

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

<s:element name="e1">

<s:complexType />

</s:element>

<s:element name="abcResponse">

<s:complexType>

<s:sequence>

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

</s:sequence>

</s:complexType>

</s:element>

<s:element name="a1" type="s0:aaa" />

<s:complexType name="aaa">

<s:sequence>

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

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

</s:sequence>

</s:complexType>

</s:schema>

</types>

<message name="i1">

<part name="parameters" element="s0:e1" />

</message>

<message name="o1">

<part name="parameters" element="s0:abcResponse" />

</message>

<message name="Va">

<part name="aaa" element="s0:a1" />

</message>

<portType name="pt">

<operation name="abc">

<input message="s0:i1" />

<output message="s0:o1" />

</operation>

</portType>

<binding name="b1" type="s0:pt">

<soap:binding transport="http://schemas.xmlsoap.org/soap/http" style="document" />

<operation name="abc">

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

<input>

<soap:body use="literal" />

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

</input>

<output>

<soap:body use="literal" />

</output>

</operation>

</binding>

<service name="sss">

<port binding="s0:b1">

<soap:address location="http://localhost:8080/qqqs/a.asmx" />

</port>

</service>

</definitions>

 

sss.cs

namespace mmm

{

using System.Diagnostics;

using System.Xml.Serialization;

using System;

using System.Web.Services.Protocols;

using System.Web.Services;

[System.Web.Services.WebServiceBindingAttribute(Name="b1", Namespace="http://tempuri.org/")]

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

public aaa a1;

public sss()

{

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

}

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

[System.Web.Services.Protocols.SoapDocumentMethodAttribute("http://tempuri.org/abc", RequestElementName="e1", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]

public string abc()  {

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

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

}

}

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

public class aaa : SoapHeader {

public string User;

public string Password;

}

}

 

Client a.bat

del *.dll

del *.cs

wsdl aa.wsdl /namespace:mmm

csc.exe /t:library  sss.cs

copy sss.dll .\bin

 

web.config

<configuration>

<system.web>

<compilation>

<assemblies>

<add assembly="System.Security"/>

</assemblies>

</compilation>

<httpModules>

<add name="w1" type="yyy, zzz" />

</httpModules>

</system.web>

</configuration>

 

global.asax

<%@ Language="C#" %>

<%@ Import Namespace="System.Security.Principal" %>

<%@ Import Namespace="System.IO" %>

<script runat=server>

public void w1_OnAuthenticate(object o, eee e) {

abc("global.asax w1_OnAuthenticate");

if ((e.Us == "vijay") && (e.Pass == "mukhi"))

{

string [] s = new string[1];

s[0] = "Customer";

e.qqq(s);

}

}

public void abc(string s) {

FileStream fs = new FileStream("c:\\a.txt", FileMode.Append, FileAccess.Write);

StreamWriter w = new StreamWriter(fs);

w.WriteLine(s);

w.Flush();

w.Close();

}

</script>

 

a.asmx

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

using System;

using System.IO;

using System.Web.Services;

using System.Web.Services.Protocols;

public class a1 : SoapHeader {

public string User;

public string Password;

}

public class ttt : WebService {

public a1 a;

[WebMethod]

[SoapHeader("a")]

public string abc()

{

abc("asmx abc");

if (User.IsInRole("Customer"))

return "Role customer";

if (User.Identity.IsAuthenticated)

return "Valid user";

return "Not Authenticated";

}

public void abc(string s) {

FileStream fs = new FileStream("c:\\a.txt", FileMode.Append, FileAccess.Write);

StreamWriter w = new StreamWriter(fs);

w.WriteLine(s);

w.Flush();

w.Close();

}

}

 

zzz.cs

using System;

using System.Web;

using System.Security.Principal;

using System.IO;

using System.Xml;

using System.Xml.XPath;

using System.Web.Services.Protocols;

using   System.Collections.Specialized;

public class eee: EventArgs {

IPrincipal pu;

HttpContext c;

string    u;

string    p;

public eee(HttpContext co, string us, string pass)

{

abc("eee 1");

c = co;

u = us;

p = pass;

}

public  HttpContext Cont

{

get  {

abc("Context ");

return c;

}

}

public IPrincipal Prin

{

get {

abc("get Principal ");

return pu;

}

set {

abc("set Principal ");

pu = value;

}

}

public void qqq(string[] roles)

{

abc("qqq 1");

GenericIdentity i = new GenericIdentity(Us);

Prin = new GenericPrincipal (i, roles);

}

public string Us

{

get {

abc("get User ");

return u;

}

set {

abc("set User ");

u = value;

}

}

public string Pass

{

get

{

abc("get Password");

return p;

}

set

{

abc("set Password");

p = value;

}

}

public void abc(string s)

{

FileStream fs = new FileStream("c:\\a.txt", FileMode.Append, FileAccess.Write);

StreamWriter w = new StreamWriter(fs);

w.WriteLine(s);

w.Flush();

w.Close();

}

}

public delegate void ddd(Object sender,  eee e);

public sealed class yyy : IHttpModule

{

ddd e = null;

public event ddd Authenticate

{

add

{

abc("yyy Authenticate Add");

e += value;

}

remove

{

e -= value;

abc("6");

}

}

public void Init(HttpApplication a)

{

abc("yyy Init");

a.AuthenticateRequest += new EventHandler(xyz);

}

void xyz(Object o, EventArgs ev)

{

abc("yyy xyz");

HttpApplication a = (HttpApplication)o;

HttpContext co = a.Context;

Stream s = co.Request.InputStream;

long p = s.Position;

abc(p.ToString());

string s3 = "";

int i;

NameValueCollection c;

HttpRequest r = co.Request;

c=r.Headers;

string[] s1 = c.AllKeys;

abc(s1.Length.ToString());

for (i = 0; i<s1.Length; i++)

{

s3 = "Key: " + s1[i] + "=";

string[] s2=c.GetValues(s1[i]);

abc(s3 + s2[0]);

}

if (co.Request.ServerVariables["HTTP_SOAPACTION"] == null)

return;

XmlDocument d1 = new XmlDocument();

string u;

string pa;

try

{

d1.Load(s);

s.Position = p;

d1.Save("C:\\b.txt");

s.Position = p;

XmlNodeList no = d1.GetElementsByTagName("User");

XmlNode n = no[0];

u = n.InnerText;

pa = d1.GetElementsByTagName("Password").Item(0).InnerText;

abc(u + " " + pa);

}

catch (Exception ee)

{

s.Position = p;

XmlQualifiedName na = new XmlQualifiedName("Sonal");

SoapException so = new SoapException( "Error in SOAP", na, ee);

throw so;

}

eee e1; 

e1 = new eee(co, u , pa);

e(this, e1);

if (e1.Us != null)

e1.Cont.User = e1.Prin;

return;

}

public void abc(string s)

{

FileStream fs = new FileStream("c:\\a.txt", FileMode.Append, FileAccess.Write);

StreamWriter w = new StreamWriter(fs);

w.WriteLine(s);

w.Flush();

w.Close();

}

public void Dispose()

{

}

}

 

Server a.bat

csc /t:library zzz.cs

copy zzz.dll .\bin

 

a.txt

yyy Init

yyy Authenticate Add

yyy xyz

0

6

Key: Content-Length=442

Key: Content-Type=text/xml; charset=utf-8

Key: Expect=100-continue

Key: Host=localhost

Key: User-Agent=Mozilla/4.0 (compatible; MSIE 6.0; MS Web Services Client Protocol 1.0.2914.16)

Key: SOAPAction="http://tempuri.org/abc"

vijay mukhi

eee 1

global.asax w1_OnAuthenticate

get User

get Password

qqq 1

get User

set Principal

get User

Context

get Principal

asmx abc

yyy xyz

vijay mukhi111

eee 1

global.asax w1_OnAuthenticate

get User

get Password

get User

Context

get Principal

asmx abc

 

b.txt

<?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>

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

      <User>vijay</User>

      <Password>mukhi</Password>

    </a1>

  </soap:Header>

  <soap:Body>

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

  </soap:Body>

</soap:Envelope>

 

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>

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

  <User>vijay</User>

  <Password>mukhi</Password>

  </a1>

  </soap:Header>

- <soap:Body>

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

  </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>Role Customer</abcResult>

  </abcResponse>

  </soap:Body>

  </soap:Envelope>

 

This is our single largest program so far, which is a reason enough for you to follow our instructions to the T. Firstly, create a folder qqq in the root, and then within it, create two more folders named 'client' and 'server'. Stay put! Now, you need to create a bin folder within each of the 'client' and 'server' subdirectories.

 

After having created the required physical subdirectories, we then have to create two virtual directories:

     qqqc - it points to folder c:\qqq\client

     qqqs - it points to c:\qqq\server

 

The screens displayed below would facilitate the creation of these virtual directories.

 

Screen 11.1

 

Screen 11.2

 

Screen 11.3

Screen 11.4

 

Screen 11.5

Screen 11.6

 

Screen 11.7

Screen 11.8

 

Then, write the above a.aspx program in the c:\qqq\client subdirectory. First of all, we set the 'debug' mode to 'true', in order that the error messages, if any, become visible.

 

Almost the entire code is placed in namespaces. Here, the namespace mmm contains the code. Hence, we import a namespace called mmm. Finally, at the end of the aspx file, exist two spans:

     The first called s1, which displays the return value.

     The second s2, which displays the error message, if any.

 

The Page_Load function gets called immediately, prior to the page being sent across. It merely initializes the InnerHtml property of the spans to 'null', so that they do not display anything.

 

Then, there exist two textboxes named 'u' and 'p', together with a button. The user is required to enter the username and password, and then, click on the button. When the button is clicked, the function abc gets called. This function gets executed on the server, and creates two objects:

     One is an instance of the class sss.

     The other is an instance of the class aaa.

 

As is evident, these classes are user-defined classes that are present in a dll, which is placed in the bin folder, within the client subdirectory. The aaa class has two members named User and Password, which are initialized to the values entered in the textboxes 'u' and 'p'. Thereafter, the 'a1' member of the sss class is initialized to this object 'a', which is an instance of the class aaa. The abc function is called next from sss, and the return value is stored in a string named 'r'.  The InnerHtml property of the span is then set to 'r', to facilitate the value to be finally displayed in the browser window.

 

If an exception is thrown by the function abc, the code in the 'catch' block gets called, whereupon, the exception message is displayed using the span, with the id of s2. Thus, it is either the return value of the function abc or the exception message which is displayed in the browser.

 

 

The aa.wsdl file generates the code for the function abc. So, let us scurry through the wsdl file. Most of it has already been expounded earlier. As always, the file begins with the root element definitions, which are followed by a number defined namespaces. Then, an element e1 is created, which is empty in nature. Another element abcResponse is also created, containing a single element named abcResult.

 

The element a1 of type aaa contains two elements, viz. User and Password. The type aaa is responsible for initiating the class aaa in the client, which is eventually employed to send across the header. The message i1 has one part, containing an element e1. Since this element is devoid of any contents, the part and the message have no contents, whatsoever. The other part is assigned to the second message o1, which represents the return packet from the server. The element of the second part is abcResponse, which is a string.

 

The header is represented by the message va, which has an element called a1. This in turn, contains the two header elements. The portType 'pt' represents the operation abc, where the input is represented by the message i1, and the output is represented by the message o1. To summarize, abc does not accept any input, and returns its output in the form of a string.

 

The binding b1 accepts the portType pt. The style of the binding is 'document', and a literal is used for the input body element. Thus, in the input element, the SOAP packet is designed to hold a Body, as well as a header that contains the message named Va. Finally, the service is assigned the name 'sss'. Therefore, the names assigned to the client file and the class will also be sss. The binding used is 'b1', and the address element specifies the complete url of the code, which is as follows: http://localhost:8080/qqqs/a.asmx.

 

There are no surprises in store for you in sss.cs, or in the client program generated by the WSDL program. The header class is aaa, and the variable that carries the header is a1.

 

The abc function is bestowed with a million attributes from the SoapDocumentMethod attribute; however currently, none of these are of any significance, since the function abc has been called without any parameters. Resultantly, the encoding that is used, is also meaningless. The batch file a.bat merely calls the wsdl program, in order to generate the proxy file named sss.cs.

 

The csc compiler compiles the sss.cs file into a dll. This dll is then copied into the bin folder, since it is peremptory for the entire code that is called from the aspx file, to dwell in the bin subdirectory. We reiterate it yet again, that the following files should reside in the folder c:\qqq\client, and the dll should be placed in the bin subdirectory.  Thus, the files and their corresponding paths are as follows:

 

\qqq\client\a.aspx

\qqq\client\aa.wsdl

\qqq\client\bin\sss.dll

 

Having dealt with the client, let us now progress on to the files that are required at the server's side. The following files will reside in the folder c:\qqq\server:

 

\qqq\server\web.config

\qqq\server\global.asax

\qqq\server\a.asmx

\qqq\server\zzz.cs

\qqq\server\bin\zzz.dll

 

The very first file that we anatomize is the web.config file. This file is a pure XML file, and contains settings that determine the configuration that the web server would use. The file must be present in the root directory of the web server, since the server utilizes the settings that are located in the file. So, this file is created in the directory c:\qqq\server.

 

The root tag configuration is followed by a system.web element. This element boasts of two children, viz. a compilation section and an httpModules section. As the name itself suggests, the compilation section contains all the compilation settings. However, in this case, there is only one setting named 'assemblies'. The 'assemblies' section signals the need for an assembly System.Security, as it is a point of reference for the code included in any file. Then, we encounter the httpModules element, which provides succor in configuring the HttpModules that have been used in the application. One of these modules gets added to our application.

 

The type attribute has two entities, separated by a comma. The first is the name of a class, i.e. yyy, while the second is the name of the dll, i.e. zzz.dll. The file zzz.dll contains the code and encloses the class yyy within it. This arrangement is termed as a class/assembly combo. The attribute name is specified as w1, which is used to refer to code in the assembly zzz.dll.

 

We would be examining the file global.asax in a short while from now.

 

Now, let us investigate the output file a.txt, which comprises of the particulars about the functions that are called, and the details as regards when they get called. The first function to be called is Init, which belongs to the class yyy, and resides in zzz.cs. The zzz.dll file is located in the server\bin folder. The class yyy is fabricated as a sealed class, since we do not want anyone to derive from it.

 

This class is derived from the Interface IHttpModule, which contains only two methods, Init and Dispose. The Init function prepares the yyy class to receive requests, while the Dispose function is used for the winding up tasks.

 

The Init function is passed an object of type HttpApplication as a parameter. This class has methods, properties and events that are required for representing application objects in the ASP.Net world. All that we do in the Init function is, to set the event AuthenticateRequest to the function xyz. Thus, whenever a request needs to be authenticated, the function xyz gets called.

 

Next, we have a property called Authenticate, which is called by the framework. Since this property is of type event, it does not have a 'get' or 'set' accessor, instead, it has an 'add' and a 'remove'.

 

Whenever a function is to be added to the event, the add accessor is called. When a function is to be removed, the remove accessor is called. The value member contains the function that is to be added or removed. Before long, we would be able to discern which function is being added to the accessor.

 

The property has a type ddd, which is a delegate that takes two parameters, viz. an Object and an eee class. The eee class is derived from EventArgs, and is the first class in the zzz.cs file. None of the functions would ever get called, if the code in the Init function was placed within comments.

 

After Init and Authenticate, the next function to be called is xyz. The first parameter to this function is an HttpApplication object, and the second parameter is an object, which is derived from EventArgs. This parameter is seldom accessed.

 

The HttpApplication object has a member Context, which represents the HTTP context in which we operate. The value of the context is stored in a variable called 'co'. It is indeed possible for us to write an entire book with effortless ease, expounding all the workings of an HttpContext object; however, we have chosen to refrain from it. What we require is a property Request of type HttpRequest, which contains a property named InputStream, of type Stream. This stream object is stored in 's', and it is used for reading the entire SOAP payload passed to the function.

 

But prior to that, we determine the current position of the file pointer by displaying the Position parameter. The a.txt file unravels the fact that, the pointer is located at the very beginning. We store this value zero in a variable p, which we would be using subsequently.

 

The code that is inserted next, is very similar to the program that was used to display the headers, when an HttpRequest object was supplied. There are a total of six headers, whose names and values are entered into the file a.txt.

 

The header HTTP_SOAPACTION confirms the fact that the payload is actually a SOAP payload. Using the name of the header as an indexer to the ServerVariables property, we ascertain the value embodied in it. If the value is null, the request is not regarded as a SOAP formatted request; instead, it is treated as a normal HTTP request.

A SOAP payload is an XML document. Therefore, an empty XmlDocument object d1 is created. Then, the data that is associated with the Stream 's' or the SOAP payload, is loaded with the help of the Load function. The Position property of the XML document is reverted back to the starting position.

 

Finally, using the Save function, the SOAP payload is written to disk in the file b.txt. This file, when viewed, is espied to be identical to the SOAP request that is sent across. The SOAP request has a header that contains the user name and password instead of it being shown in the body.

 

Now, we use a deviant approach to retrieve the values, since we do not intend to implement the method that was used earlier.

 

The function GetElementsByTagName accepts a parameter that signifies the name of the element that we are interested in, viz. User. This function returns a list of nodes in XmlNodeList, which contains the element User. Since there is only one element called User, we use the indexer to access this XmlNode object 'n'.

 

The InnerText property then facilitates access to the content. The value retrieved is stored in the variable u. By using a similar mechanism, the contents of the element password are retrieved. The values that are displayed, provide ample evidence of our treading the right track. However, if things go adrift; for instance, if the User and Password elements do not exist in the header, a full-fledged SOAP Exception will be sent across.

 

Next, we create an object that bears a resemblance to class eee. This class is derived from the class EventArgs, and hence, it can be used as the second parameter in all the event handling code, which needs an object derived from EventArgs. The object which looks like eee, is created by calling its constructor and assigning it three parameters, the HttpContext object 'co', the strings 'u'  and 'pa' .

 

They contain the user name and password, both of which have been extracted from the header. Please note that the code in the asmx file will not be called anywhere in the near future.

 

The constructor of the class eee, stores the values passed in the three variables c, u and p. The variables are also used in the properties of Cont, Us and Pass. The variables merely act as fronts for the properties.

 

Along the same lines, the variable 'p' is used for the property Prin. If we so desire, we can make the variables public, and replace the property Us with u.  But, it has been reiterated time and again that, it is the properties that are to be used, and not variables. Albeit, a variable could have served our purpose equally well, in the above case. As the saying goes, " Old habits die hard!"

 

Now, one of the functions is called, using the event e.

 

So far, we were oblivious to the identity of the function that was passed in the value parameter. However, it is about time that we lifted the veil off the mystery. The function's name is w1_OnAuthenticate, and it is located in the file global.asax. This function is called with two parameters:

     The first is the sender, the class 'yyy'.

     The second is the 'eee' object, which stores the HttpContext object, the user name and the password.

 

This file is called the ASP.NET application file. It contains code that responds to application level events that occur.

 

As was the case with the config file that was discussed earlier, the global.asax file ought to be residing in the root directory of the web server, in order  to get a right output of the application. This file is compiled into the class generated by the framework. Since the web server completely forbids access to this file, no one can gain access to its contents by using a URL. This file contains all the application or session handlers.

 

The function name in the asax file has to begin with the name 'w1', followed by the underscore symbol, finally followed by the name of the event 'OnAuthenticate'. The second parameter 'eee' is used to verify the user name and password, using the properties Us and Pass. These in turn, refer to the properties of Us and Pass.

 

If the SOAP header contains the user name 'vijay' and the password 'mukhi', then and only then, does the function qqq get called in the 'eee' class. To put it differently, it implies that if the user name and password are any different from the ones that have been specified above, the qqq function will not be called. This function is passed an array of strings, having a size of 1, since it contains only a solitary string named Customer.

 

In the qqq function, we create an object i, which is an instance of class GenericIdentity. This class is derived from the interface Iidentity and it represents a user, on behalf of whom, certain code gets executed. We pass the name of the user    ('vijay' in this case), to the constructor of the GenericIdentity class.

 

Another class named GenericPrincipal is created, whose sole task is to check the membership of the user, in a set of roles. Thus, the constructor is provided with an array of role names that the user belongs to. Our user belongs to a single role of 'Customer'. The Principal is then stored in the property Prin.

           

Now, let us refocus on the class yyy and the function xyz. Here, the property Us, which contains the user name, is verified to determine if it is null or not. In this case, the user name is not null. Therefore, the principal object that we created in the qqq function is first retrieved. Thereafter, using the Cont property, the HttpContext object that is passed to the eee object, through the eee constructor, is retrieved. This Context object has a property User of type IPrincipal, which stipulates the security information for the HTTP request. This IPrincipal object belongs to the singular role named Customer.

 

Once the function xyz accomplishes its task, the function abc gets called from the asmx file. Thus, it is the final function to be called in the sequence. Here, we merely use the same User object, which is set to the IPrincipal object, and call the function IsInRole. As the role is Customer, the 'if' statement results in true. Then, the password is changed to 'mukhi11'.

 

At this stage, the qqq function does not get called. Hence, the IsInRole function returns false. Also, since the Authenticated property is not set to true, the second 'if' statement returns false. In the a.txt file, he second call after the HTTP headers is no more visible. If we had so desired, we could have changed the IsAuthenticated property to true, and depending upon the user name, attached the user to more than one group.

 

The vital point that we intend to convey through this program is that, before the asmx file appears on the scene, the code in the dll file and the asax file, gets called. Also, any changes made to the property get reflected only in the asmx file.