Web Service Architecture

for RuBoard

Besides handling ASP.NET, Microsoft's Internet Information Server (IIS) can handle Web Services, since they come in as HTTP requests. These requests are encoded in the URL or as XML. IIS then creates the required object to fulfill the Web Service request. IIS then calls the object's method that is associated with the request. Any returned values are converted to XML and returned to the client, using the HTTP protocol.

The Add Web Service Example

To illustrate how this works under Microsoft .NET, we will build a simple Web Service to illustrate this architecture and how the associated protocols are used. Our Web Service will simply add two numbers . To make things clear we will build the Web Service, Add, in the simplest possible way.

By writing code in a file with the suffix asmx and placing it in a subdirectory of the IIS root directory we can have a simple Web Service. [6] IIS has the concept of virtual directories, so that the actual directory does not have to physically be under the IIS root directory. The easiest way to do this is to enable WebSharing on the file folder. Select the folder in the NT Explorer, right-click on the folder, and select Sharing on the context menu. Use the Web Sharing tab to make the directory a virtual directory for IIS.

[6] By default this directory is \inetpub\ wwwroot .

The file add.asmx first defines the language used to write the Web Service, and the class that has the definitions. That class inherits from the WebService class in the namespace System.Web.Services . Note the use of the WebService attribute to define a namespace for the service. This file is found in the WebService subdirectory of the SimpleWebService directory for this chapter. You should make WebService a virtual directory with alias SimpleWebService , as described in the previous paragraph.

A method of that class can be used as a Web Service if the attribute WebMethod is applied to it.

 <%@ WebService language="C#" class="Test" %>  using System;  using System.Web;  using System.Web.Services;  [WebService(Namespace=           "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E")]  public class Test: WebService  {    [WebMethod]    public long Add(long x, long y)    {      return x + y;    }  } 

We will put this file in a directory called SimpleWebService . [7]

[7] You can use a code-behind page here if you wish. WebServices created with VS.NET do reference a code-behind page in the asmx file.

A Client Program for the Add Web Service

Internet Explorer can be used as a simple client program that uses the HTTP GET protocol's URL encoding of a Web Service request. Using http:// localhost/SimpleWebService/Add.asmx as the address, Figure 11-1 shows the result.

Figure 11-1. Web Service request in Internet Explorer.

graphics/11fig01.gif

By clicking on the Add link you will get a form enabling you to submit a request to the Add Service. In addition, the form describes the various HTTP protocols that can be used for submitting the request. For our purposes, two protocols are worth mentioning: HTTP GET and SOAP.

The HTTP GET protocol is worth exploring because the form that appears in IE uses it. The protocol has boldfaced placeholders for data that has to be entered:

 GET /SimpleWebService/add.asmx/Add?  x  =  string  &  y  =  string  HTTP/1.1  ... 

The data entered into the form is added to the URL in the standard way that any HTTP GET request is made. Data are returned as:

 ...  <long xmlns="urn:uuid:10C14FCF-BF4A-477a-BFE7-                                  41B9F2A4514E">  long  </long> 

Figure 11-2 shows values entered into the form. By pressing the Invoke button, you can call the Web Service.

Figure 11-2. Values entered on the Internet Explorer form.

graphics/11fig02.gif

An IE window will appear with the part of the HTTP response data generated by the Web Service that contains the actual returned value:

 ...  <long xmlns=    "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E">  9  </long> 

This is exactly the format that appeared in the description of the protocol with the answer (9) substituted for the placeholder. HTTP GET, however, can handle only simple types.

The more interesting protocol is SOAP. Both the SOAP HTTP POST request and response are described with placeholders for information that has to be provided in the actual call. Those placeholders are in boldface type.

First, let us look at the SOAP HTTP POST request. The first part is a set of HTTP headers. The XML for the SOAP protocol is in the data (entity-body) section of the HTTP request, which is always separated from the headers by a blank line. The content-length header is the length of the data, which is dependent on the size of the parameters in the data section.

The method header identifies the file to which the request is directed. It could also name an object that is to handle the request (endpoint). The SOAPAction header indicates the name of the method, qualified by a namespace, to be invoked for the Web Service. [8]

[8] For those with a COM background, you can think of the namespace for the method as equivalent to the GUID that identifies and interface (IID).

SOAP uses XML to specify the parameters of the method. [9] The SOAP body contains the parameters for the method call. In a real method call, the long placeholders would be replaced by the actual parameters to be passed to the Web Service method.

[9] The parallel to IDL is WSDL, which we will discuss shortly. SOAP is analogous to NDR, the wire format used for DCOM calls. All these parallels to COM appear in Don Box's March 2000 MSDN article "A Young Person's Guide to The Simple Object Access Protocol."

 POST /SimpleWebService/Add.asmx HTTP/1.1  Host: localhost  Content-Type: text/xml; charset=utf-8  Content-Length:  length  SOAPAction:         "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E/Add"  <?xml version="1.0" encoding="utf-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=            "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E">        <x>  long  </x>        <y>  long  </y>      </Add>    </soap:Body>  </soap:Envelope> 

Next the HTTP response is described. The long placeholder will be replaced by the actual value returned.

 HTTP/1.1 200 OK  Content-Type: text/xml; charset=utf-8  Content-Length:  length  <?xml version="1.0" encoding="utf-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>      <AddResponse xmlns=            "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E">        <AddResult>  long  </AddResult>       </AddResponse>    </soap:Body>  </soap:Envelope> 

WSDL

SOAP does not describe the Web Service interface. While you could encode the SOAP yourself, it would be nice to be able to generate proxy classes for the client to use. Otherwise you would have to understand all the details of the SOAP specification and how to parse the returned XML.

WSDL provides a description of the Web Service interface. Here is the WSDL description for our SimpleWebService which has one method, Add . We have omitted the WSDL for invocations of the Web Service that do not use SOAP. The <types> section defines the types:

  • Add is used in when SOAP invokes the Web Service.

  • AddResponse is used when the SOAP Web Service invocation returns.

Add has two elements, each occurring exactly once. Both are defined with the XSD type long, and they have the names x and y. The return parameter, whose name is AddResponse , has one element which occurs once named AddResult defined with the XSD type long. Note how these types were used in the SOAP definitions we looked at previously.

 ...  <types>  ...    <s:element name="Add">      <s:complexType>        <s:sequence>          <s:element minOccurs="1" maxOccurs="1" name="x"                                            type="s:long" />          <s:element minOccurs="1" maxOccurs="1" name="y"                                            type="s:long" />        </s:sequence>      </s:complexType>    </s:element>    <s:element name="AddResponse">      <s:complexType>        <s:sequence>          <s:element minOccurs="1" maxOccurs="1"                           name="AddResult" type="s:long" />        </s:sequence>      </s:complexType>    </s:element>    ...  </types> 

The <message> section relates the types to their use as parameters.

 <message name="AddSoapIn">    <part name="parameters" element="s0:Add" />  </message>  <message name="AddSoapOut">    <part name="parameters" element="s0:AddResponse" />  </message>  ... 

The <portType> section relates the Web Service to the individual Web methods defined by the <operation> elements. If there had been more Web methods in the Web Service, there would have been more operation elements associated with the portType. [10] Each method's input and output operation is associated with the appropriate message defined previously.

[10] For those of you keeping score, this is analogous to a COM interface.

 <portType name="TestSoap">    <operation name="Add">      <input message="s0:AddSoapIn" />      <output message="s0:AddSoapOut" />    </operation>  </portType>  ... 

The <binding> section defines the encodings and protocols to be used for each operation.

 <binding name="TestSoap" type="s0:TestSoap">    <soap:binding            transport="http://schemas.xmlsoap.org/soap/http"            style="document" />    <operation name="Add">      <soap:operation soapAction=         "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E/Add"         style="document" />      <input>        <soap:body use="literal" />      </input>      <output>        <soap:body use="literal" />      </output>    </operation>  </binding>  ... 

The <service> section relates the Web Service to its port and how it is invoked.

 <service name="Test">    <port name="TestSoap" binding="s0:TestSoap">      <soap:address location=             "http://localhost/SimpleWebService/Add.asmx" />     </port>  ...  </service>  ... 

Proxy Classes

The wsdl tool can be used to read the WSDL description and generate a proxy class that will make the SOAP calls for you. Since C# is the default language, and SOAP the default protocol, the following command will generate a proxy class file with the name addproxy.cs :

 wsdl /out:addproxy.cs             http://localhost/SimpleWebService/Add.asmx?WSDL 

The generated proxy defines a constructor and three methods. The constructor sets the URL which this Web Service uses. One of the methods represents a synchronous, blocking call on the Web Service. The other two methods correspond to the asynchronous design pattern discussed in Chapter 8. If you want to call the Web Service asynchronously you can use the BeginXXX and the EndXXX methods associated with the proxy. [11] The proxy class has the same name as the WebService class.

[11] Of course in this particular case XXX=Add.

The Invoke method of the SoapHttpClientProtocol class will make the HTTP request and process the HTTP response associated with the transmitted and received SOAP packets. This example is found in the SimpleAddClient subdirectory under the SimpleWebService directory.

 ...  public class Test :      System.Web.Services.Protocols.SoapHttpClientProtocol  {    ...    public Test()    {      this.Url ="http://localhost/SimpleWebService/Add.asmx";    }    ...    public long Add(long x, long y)    {      object[] results = this.Invoke("Add",                                       new object[] {x, y});      return ((long)(results[0]));    }     ...    public System.IAsyncResult BeginAdd(long x, long y,            System.AsyncCallback callback, object asyncState)    {          return this.BeginInvoke("Add",                 new object[] {x, y}, callback, asyncState);    }    ...    public long EndAdd(System.IAsyncResult asyncResult)    {          object[] results = this.EndInvoke(asyncResult);          return ((long)(results[0]));    }  } 

You can then write a program to use the proxy classes to issue a Web Service request.

 public class AddClient  {    public static void Main(string[] args)    {      Test z = new Test();      long f = z.Add(1, 2);      Console.WriteLine(f);      return;    }  } 

Web Service Client with Raw SOAP and HTTP

To show you what the SoapHttpClientProtocol class does, the final client program for this example uses sockets to send both the HTTP headers and the SOAP directly and to receive the response from the Web Service. This example is the RawAddClient subdirectory of the SimpleWebService .

The main routine first reads in a file that has the SOAP headers for the service to be called. It returns the length of the content, which will have to be placed in one of the HTTP POST headers.

 long contentLength;  StringBuilder contentData = BuildContent("SoapAdd.txt",                                        out contentLength);  StringBuilder requestHeader = BuildHeader(contentLength); 

It then connects to the server, sends the data, and receives the response, which it writes out to the console.

 IPEndPoint endPoint = new          IPEndPoint(Dns.Resolve(httpServer).AddressList[0],                                                  httpPort);  Socket sock = new Socket(AddressFamily.InterNetwork,                        SocketType.Stream, ProtocolType.Tcp);  sock.Connect(endPoint);  ...  sock.Send(header, header.Length, 0);  sock.Send(content, content.Length, 0);  ...  bytes = sock.Receive(receivedData, receivedData.Length,                                                      0);  Console.WriteLine(ASCII.GetString(receivedData, 0,                                                 bytes));  sock.Close();  ... 

The routine BuildHeader just builds a standard HTTP POST request with the addition of the SOAPAction header.

 StringBuilder sb = new StringBuilder(1024);  sb.Append("POST /SimpleWebService/Add.asmx HTTP/1.1\r\n");  sb.Append("Host: localhost\r\n ");  sb.Append("Content-Type: text/xml; charset=utf-8 \r\n");  string line = "Content-Length: " +                           contentLength.ToString() + "\r\n"  sb.Append(line);  sb.Append("SOAPAction: \"urn:uuid:          10C14FCF-BF4A-477a-BFE7-41B9F2A4514E/Add\"\r\n ");  sb.Append("\r\n");  ... 

BuildContent just reads a file to a buffer and calculates the size of the buffer in bytes.

 contentLength = 0;  String line;  while ((line = fileStream.ReadLine()) != null)  {    sb.Append(line);    sb.Append("\r\n");    contentLength += line.Length + 2;  }  fileStream.Close();  ... 

Based on our previous discussion, the SOAP file, SoapAdd.txt , looks as we would expect it to. The input parameters "9" and "3" appear as the WSDL would dictate .

 <?xml version="1.0" encoding="utf-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=            "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E">        <x>9</x>        <y>3</y>      </Add>    </soap:Body>  </soap:Envelope> 

The program first writes out the HTTP POST request. First come the standard HTTP headers with a special SOAPAction header, then the SOAP encoding of the request.

 POST /SimpleWebService/Add.asmx HTTP/1.1  Host: localhost  Content-Type: text/xml; charset=utf-8  Content-Length: 393  SOAPAction:         "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E/Add"  <?xml version="1.0" encoding="utf-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=            "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E">        <x>9</x>        <y>3</y>      </Add>    </soap:Body>  </soap:Envelope> 

The program then writes out the response. Again, the HTTP headers come first, then the SOAP encoding of the result, "12."

 ...  HTTP/1.1 200 OK  Server: Microsoft-IIS/5.0  Date: Mon, 17 Sep 2001 02:11:30 GMT  Cache-Control: private, max-age=0  Content-Type: text/xml; charset=utf-8  Content-Length: 383  <?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>      <AddResponse xmlns=             "urn:uuid:10C14FCF-BF4A-477a-BFE7-41B9F2A4514E">        <AddResult>12</AddResult>      </AddResponse>    </soap:Body>  </soap:Envelope> 
for RuBoard


Application Development Using C# and .NET
Application Development Using C# and .NET
ISBN: 013093383X
EAN: 2147483647
Year: 2001
Pages: 158

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net