unimportant logo identifying Barry Cornelius

University of Oxford

Computing Services

Web Services using .NET

  Author: Barry Cornelius Date: 6th May 2004; first created: 31st January 2002


1. Introduction

Here is some background to the talk:

This talk has the following aims:

Of course, some of you may already know about .NET and Web Services.

2. What is a Web Service?

2.1. A definition

2.2. An example

In their book [35] about the .NET Framework, Thai and Lam provide the following answer to the question How can software be viewed as services?:

3. Using .NET to provide a Web Service

3.1. Providing the code to be executed

The file Service1.asmx.cs contains some code in C#. All we have to do is to add the code of each method of our Web Service to the Service1.asmx.cs file. So we could add the method:

0001: [WebMethod]
0002: public double ToFahrenheit(double pCentigrade)
0003: {
0004:     return 32 + pCentigrade*9/5;
0005: }
As shown above, we have to supply a WebMethod attribute for each method of the class that we wish to be accessible through the Web Service.

Such methods are declared in a class that is derived from the class WebService of the System.Web.Services namespace. It is also usual to include a WebService attribute:

0006: [WebService]
0007: public class Service1 : System.Web.Services.WebService
0008: {
0009:     ...
0010: }

Note: if this class provides other methods that do not have a WebMethod attribute, they will not be (directly) accessible by an external site.

Here is an example of the code that is generated by Visual Studio.NET's wizard. I only typed in lines 24-25 and lines 61-65:

0011: using System;
0012: using System.Collections;
0013: using System.ComponentModel;
0014: using System.Data;
0015: using System.Diagnostics;
0016: using System.Web;
0017: using System.Web.Services;
0019: namespace ServerConvert
0020: {
0021:     /// <summary>
0022:     /// Summary description for Service1.
0023:     /// </summary>
0024:     [WebService(Namespace="http://www.a.com/webservices/",
0025:          Description="This Web Service provides temperature conversion services.")]
0026:     public class Service1 : System.Web.Services.WebService
0027:     {
0028:         public Service1()
0029:         {
0030:             //CODEGEN: This call is required by the ASP.NET Web Services Designer
0031:             InitializeComponent();
0032:         }
0034:         #region Component Designer generated code
0036:         //Required by the Web Services Designer 
0037:         private IContainer components = null;
0039:         /// <summary>
0040:         /// Required method for Designer support - do not modify
0041:         /// the contents of this method with the code editor.
0042:         /// </summary>
0043:         private void InitializeComponent()
0044:         {
0045:         }
0047:         /// <summary>
0048:         /// Clean up any resources being used.
0049:         /// </summary>
0050:         protected override void Dispose( bool disposing )
0051:         {
0052:             if(disposing && components != null)
0053:             {
0054:                 components.Dispose();
0055:             }
0056:             base.Dispose(disposing);        
0057:         }
0059:         #endregion
0061:         [WebMethod(Description="This converts from Centigrade to Fahrenheit.")]
0062:         public double ToFahrenheit(double pCentigrade)
0063:         {
0064:             return 32 + pCentigrade*9/5;
0065:         }
0066:     }
0067: }

The above code gives more complicated forms of the WebMethod and WebService attributes. For example, they provide information about the services that are being offered.

3.2. Providing a WWW page for accessing the code

3.3. Waiting for requests to come in

3.4. Testing the Web Service

4. Writing .NET programs to access a Web Service

4.1. Introduction

4.2. Using a wizard/tool to generate a proxy class

4.3. Writing a program that uses the proxy class

When we are providing a client of a Web Service, all we have to do is to create an instance of the proxy class that the wizard/tool has generated, and then apply the appropriate method to that object:

0087:             Service1 tService1 = new Service1();
0088:             double tFahrenheit = tService1.ToFahrenheit(tCentigrade);

4.4. Providing a console application that calls ToFahrenheit

So, here is a console application that calls the ToFahrenheit method:

0075: using Console  = System.Console;
0076: using Service1 = ConsoleConvert.Proxy.Service1;
0077: namespace ConsoleConvert
0078: {
0079:     public class Class1
0080:     {
0081:         public static void Main()
0082:         {
0083:             Console.Write("Type in a Centigrade value: ");
0084:             string tCentigradeString = Console.ReadLine();
0085:             double tCentigrade = double.Parse(tCentigradeString);
0086:             Console.WriteLine("Centigrade value is: " + tCentigrade);
0087:             Service1 tService1 = new Service1();
0088:             double tFahrenheit = tService1.ToFahrenheit(tCentigrade);
0089:             Console.WriteLine("Fahrenheit value is: " + tFahrenheit);
0090:         }
0091:     }
0092: }

4.5. Looking at the code of a method of the proxy class

When the ToFahrenheit method of the proxy class is called, its call of Invoke sends the appropriate HTTP request to the Web Service's site and decodes the reply that is returned. Then, an appropriate value is returned to the caller of the ToFahrenheit method.

4.6. The full code of the proxy class

Here is the full code of the proxy class that was generated by Visual Studio.NET:

0106: //------------------------------------------------------------------------------
0107: // <autogenerated>
0108: //     This code was generated by a tool.
0109: //     Runtime Version: 1.1.4322.573
0110: //
0111: //     Changes to this file may cause incorrect behavior and will be lost if 
0112: //     the code is regenerated.
0113: // </autogenerated>
0114: //------------------------------------------------------------------------------
0116: // 
0117: // This source code was auto-generated by Microsoft.VSDesigner, Version 1.1.4322.573.
0118: // 
0119: namespace ConsoleConvert.Proxy {
0120:     using System.Diagnostics;
0121:     using System.Xml.Serialization;
0122:     using System;
0123:     using System.Web.Services.Protocols;
0124:     using System.ComponentModel;
0125:     using System.Web.Services;
0127:     /// <remarks/>
0128:     [System.Diagnostics.DebuggerStepThroughAttribute()]
0129:     [System.ComponentModel.DesignerCategoryAttribute("code")]
0130:     [System.Web.Services.WebServiceBindingAttribute(Name="Service1Soap",
0131:         Namespace="http://www.a.com/webservices/")]
0132:     public class Service1 : System.Web.Services.Protocols.SoapHttpClientProtocol {
0134:         /// <remarks/>
0135:         public Service1() {
0136:             this.Url = 
0137:             "http://www.a.com/wsud/cs/ServerConvert/Service1.asmx";
0138:         }
0140:         /// <remarks/>
0141:         [System.Web.Services.Protocols.SoapDocumentMethodAttribute
0142:             ("http://www.a.com/webservices/ToFahrenheit",
0143:             RequestNamespace="http://www.a.com/webservices/",
0144:             ResponseNamespace="http://www.a.com/webservices/",
0145:             Use=System.Web.Services.Description.SoapBindingUse.Literal,
0146:             ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Wrapped)]
0147:         public System.Double ToFahrenheit(System.Double pCentigrade) {
0148:             object[] results = this.Invoke("ToFahrenheit", new object[] {
0149:                         pCentigrade});
0150:             return ((System.Double)(results[0]));
0151:         }
0153:         /// <remarks/>
0154:         public System.IAsyncResult BeginToFahrenheit(System.Double pCentigrade,
0155:                 System.AsyncCallback callback, object asyncState) {
0156:             return this.BeginInvoke("ToFahrenheit", new object[] {
0157:                         pCentigrade}, callback, asyncState);
0158:         }
0160:         /// <remarks/>
0161:         public System.Double EndToFahrenheit(System.IAsyncResult asyncResult) {
0162:             object[] results = this.EndInvoke(asyncResult);
0163:             return ((System.Double)(results[0]));
0164:         }
0165:     }
0166: }

4.7. Asynchronous calls

You will see that the proxy class provides two other methods besides ToFahrenheit. If a client uses the proxy class's ToFahrenheit method, it will sit there waiting for the Web Service's webserver to reply. If, instead, the client uses the BeginToFahrenheit method of the proxy class, the call of BeginToFahrenheit will return almost immediately. In the client, the call of BeginToFahrenheit has to supply (in an argument) the method that it wants to be called when the webserver delivers its reply. That method will need to call EndToFahrenheit to get the result of the call of ToFahrenheit.

5. Communicating with a .NET Web Service

5.1. HTTP GET and HTTP POST requests

Although most WWW pages are produced by means of an HTTP GET request, some WWW pages are the result of an HTTP POST request. HTTP POST requests often occur when you click on the submit button of a web form. For example, suppose a WWW page provides the following web form:

0185: <form method="POST" action="http://www.a.com/pictures/query.php">
0186:     <input type="text" name="last"/>
0187:     <input type="text" name="first"/>
0188:     <input type="text" name="age"/>
0189:     <input type="submit" value="submit/>
0190: </form>
Suppose the visitor to this WWW page types some values (say Bloggs, Fred and 27) into the three textboxes and then clicks the submit button. Because the form has a method attribute of "POST", an HTTP POST request will be generated:
0191: POST /pictures/query.php HTTP/1.1
0192: Host: www.a.com
0193: Content-Type: application/x-www-form-urlencoded
0194: Content-Length: XXXX
0196: last=Bloggs&first=Fred&age=27

5.2. Communicating with a Web Service

The .NET Framework provides three mechanisms for allowing a Web Service to be contacted:

  1. an HTTP GET request;
  2. a classical HTTP POST request;
  3. an HTTP POST request where the body of the request is some XML coded using SOAP (the Simple Object Access Protocol [40]).
The reply from the Web Service is sent as an HTTP reply usually with a body coded in XML.

5.3. Communicating with a Web Service using HTTP POST and SOAP

An example is:

0197: POST /wsud/cs/ServerConvert/Service1.asmx HTTP/1.1
0198: Host: www.a.com
0199: Content-Type: text/xml; charset=utf-8
0200: Content-Length: XXXX
0201: SOAPAction: "http://www.a.com/webservices/ToFahrenheit"
0203: <?xml version="1.0" encoding="utf-8"?>
0204: <soap:Envelope 
0205:     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
0206:     xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
0207:     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
0208:   <soap:Body>
0209:     <ToFahrenheit xmlns="http://www.a.com/webservices/">
0210:       <pCentigrade>0</pCentigrade>
0211:     </ToFahrenheit>
0212:   </soap:Body>
0213: </soap:Envelope>

Hiding in all this detail is the fact that we want to contact a webserver on www.a.com to visit the page at /wsud/cs/ServerConvert/Service1.asmx to execute the ToFahrenheit method with an argument (pCentigrade) of 0.

The reply from the webserver (after the appropriate method has been executed) is likely to be XML coded using SOAP. An example is:

0214: HTTP/1.1 200 OK
0215: Content-Type: text/xml; charset=utf-8
0216: Content-Length: YYYY
0218: <?xml version="1.0" encoding="utf-8"?>
0219: <soap:Envelope 
0220:     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
0221:     xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
0222:     xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
0223:   <soap:Body>
0224:     <ToFahrenheitResponse xmlns="http://www.a.com/webservices/">
0225:       <ToFahrenheitResult>32</ToFahrenheitResult>
0226:     </ToFahrenheitResponse>
0227:   </soap:Body>
0228: </soap:Envelope>
Hidden away in all this XML is the fact that the result of the call of the method is the value 32.

5.4. Don't panic

6. Monitoring the HTTP traffic

7. Providing more than just the basics

Various companies have been working on producing specifications for a number of areas that build on the basics of Web Services. These include:

These specifications are examined by the papers at [9, 11]. Other papers that look at the bigger picture include those at [10, 16, 29].

In the next section of this document, we just consider the first of these: WS-Security.

8. Adding security to a Web Service

8.1. More about WS-Security

Although you could use https instead of http to ensure SOAP messages are encrypted when they pass from A to B, this is regarded as inadequate. One of the problems with using https is that if B decides to send the SOAP message to some other machine C, A has no control over whether https gets used by B. So, can we do something different about security without using https?

IBM, Microsoft and Verisign have released a specification called 'WS-Security' [12, 22, 37] that provides a number of ways in which SOAP messages can be transferred securely.

For example, you can:

In this document, we will only look at the first of these: providing a username and a password.

The WS-Security specification provides three ways in which a username/password can be communicated in a UsernameToken element of the header of a SOAP message:

In this document, we will only be looking at this last possibility.

However, even if the password is encrypted, ‘an evil intermediary could [capture the information in a SOAP header and later] send the hashed password and then could be authenticated as the original sender’ [27].

So in a later proposal (called the 'WS-Security Addendum' [13]), ‘instead of sending just a hash of the password, the addendum specifies that a digest version of the password should be sent [containing] a combination of the password, a Nonce (functionally a unique string that identifies this request), and the creation time’ [27]. A server could use the Nonce and the timestamp values to ensure that replays do not take place.

8.2. Web Services Enhancements (WSE)

In December 2002, Microsoft released the 'Web Services Enhancements' (WSE) [23, 25, 26, 27, 28, 29]. This includes an implementation of the WS-Security specification (and its Addendum).

In order to use the WSE, you will need to download it and install it. The latest version is available from [24].

8.3. Modifying a server to check a username and a password

In order to use the WSE, you will need to make some changes to the web.config file that is used by the Web Service. This file is stored in the same directory as the other files of the project (e.g., Service.asmx.cs). It contains an XML document. You will need to add the following to its configuration element:

0256: <configSections>
0257:     <section 
0258:         name="microsoft.web.services"
0259:         type="Microsoft.Web.Services.Configuration.WebServicesConfiguration,
0260:                  Microsoft.Web.Services, Version=, Culture=neutral, 
0261:                  PublicKeyToken=31bf3856ad364e35" />
0262: </configSections>
0263: <microsoft.web.services>
0264:     <security>
0265:         <securityTokenManager
0266:             qname="wsse:UsernameToken"
0267:             xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext"
0268:             type="ServerUsername.MyUsernameTokenManager, ServerUsername" />
0269:     </security>
0270: </microsoft.web.services>
and the following needs to be added to the system.web element:
0271: <webServices>
0272:     <soapExtensionTypes>
0273:         <add 
0274:             type="Microsoft.Web.Services.WebServicesExtension, 
0275:                      Microsoft.Web.Services, Version=, Culture=neutral, 
0276:                      PublicKeyToken=31bf3856ad364e35" 
0277:             priority="1" 
0278:             group="0"/>
0279:     </soapExtensionTypes>
0280: </webServices>
Note: the text of each of the two type elements given above needs to be typed on one line: above, they are presented on several lines in order to help you read them.

One of the changes to the web.config file arranges for it to include a securityTokenManager element. The contents of this element ensure that ServerUsername.MyUsernameTokenManager's AuthenticateToken method gets used.

Now, whenever the Web Service is contacted, automatically behind the scenes, the WSE SOAP Extension will authenticate the usernames/passwords of any UsernameToken elements of the header of the SOAP message.

But we have to ensure that an incoming SOAP message actually has one UsernameToken element. In the following code for a Web Service providing a ToFahrenheit method, this is done by a subsidiary method called iGetUsernameToken:

0281: using PasswordOption       = Microsoft.Web.Services.Security.PasswordOption;
0282: using RequestSoapContext   = Microsoft.Web.Services.RequestSoapContext;
0283: using SecurityToken        = Microsoft.Web.Services.Security.SecurityToken;
0284: using SoapContext          = Microsoft.Web.Services.SoapContext;
0285: using SoapException        = System.Web.Services.Protocols.SoapException;
0286: using UsernameToken        = Microsoft.Web.Services.Security.UsernameToken;
0287: using UsernameTokenManager = Microsoft.Web.Services.Security.Tokens.UsernameTokenManager;
0288: using System;
0289: using System.Collections;
0290: using System.ComponentModel;
0291: using System.Data;
0292: using System.Diagnostics;
0293: using System.Web;
0294: using System.Web.Services;
0295: namespace ServerUsername
0296: {
0297:     [WebService(Namespace="http://www.a.com/webservices/",
0298:          Description="This Web Service provides temperature conversion services.")]
0299:     public class Service1 : System.Web.Services.WebService
0300:     {
0301:         ...  <<< as lines 28 to 60 on page 3 of this document >>>
0302:         [WebMethod(Description="This converts from Centigrade to Fahrenheit.")]
0303:         public double ToFahrenheit(double pCentigrade)
0304:         {
0305:             UsernameToken tUsernameToken = iGetUsernameToken();
0306:             return 32 + pCentigrade*9/5;
0307:         }
0308:         private static UsernameToken iGetUsernameToken()
0309:         {
0310:             SoapContext tSoapContext = RequestSoapContext.Current;
0311:             if (tSoapContext==null) 
0312:             {
0313:                 throw new Exception("Only SOAP requests are permitted");
0314:             }
0315:             if (tSoapContext.Security.Tokens.Count!=1)
0316:             {
0317:                 throw new SoapException("There must be one security token", 
0318:                     SoapException.ClientFaultCode);
0319:             }
0320:             foreach (SecurityToken tSecurityToken in tSoapContext.Security.Tokens)
0321:             {
0322:                 if (tSecurityToken is UsernameToken)
0323:                 {
0324:                     UsernameToken tUsernameToken = (UsernameToken)tSecurityToken;
0325:                     if (tUsernameToken.PasswordOption==PasswordOption.SendHashed)
0326:                     {
0327:                         return tUsernameToken;
0328:                     }
0329:                     else
0330:                     {
0331:                         throw new SoapException("The PasswordOption has the wrong value", 
0332:                             SoapException.ClientFaultCode);
0333:                     }
0334:                 }
0335:                 else
0336:                 {
0337:                     throw new SoapException("A UsernameToken was expected", 
0338:                         SoapException.ClientFaultCode);
0339:                 }
0340:             }
0341:             throw new SoapException("A SecurityToken has not been found",
0342:                 SoapException.ClientFaultCode);
0343:         }
0344:     }
0345:     ...  <<< as lines 241 to 255 on page 11 of this document >>>
0346: }

You will see that this code uses some types from the Microsoft.Web.Services namespace (and from some of its subnamespaces). These namespaces are a part of the WSE. In order to get Visual Studio.NET to find these types, you will need to go to the Add Reference option of its Project menu, and then use this option to add a reference to Microsoft.Web.Services.dll. This file is one of those that will have been created by installing the WSE.

Although the above code is checking that there is a UsernameToken element, it does not prevent replay attacks: it is being lazy in that it does not include code to check the Nonce (tUsernameToken.Nonce) and the timestamp value (tUsernameToken.Created) of the incoming SOAP message.

8.4. Getting a client to present a username and a password

We also have to alter the client so that it arranges for a username and a password to be present in the header of the SOAP message that it generates.

Currently, the proxy class (Service1) is derived from the class:

Service1 needs to be altered so that it is derived from the class:
The latter class is itself derived from:
and so Service1 can be used as before but it now has access to the other facilities that are in its new base class: these facilities are there to support WS-Security.

We can then add the username and the password to the header of the SOAP message by applying the appropriate method to the Service1 object:

0347: using Console        = System.Console;
0348: using Exception      = System.Exception;
0349: using PasswordOption = Microsoft.Web.Services.Security.PasswordOption;
0350: using Service1       = ConsoleUsername.Proxy.Service1;
0351: using UsernameToken  = Microsoft.Web.Services.Security.UsernameToken;
0352: namespace ConsoleUsername
0353: {
0354:     public class Class1
0355:     {
0356:         public static void Main()
0357:         {
0358:             Console.Write("Type in a Centigrade value: ");
0359:             string tCentigradeString = Console.ReadLine();
0360:             double tCentigrade = double.Parse(tCentigradeString);
0361:             Console.WriteLine("Centigrade value is: " + tCentigrade);
0362:             Console.Write("Type in a Username: ");
0363:             string tUsername = Console.ReadLine();
0364:             Console.WriteLine("Username is: " + tUsername);
0365:             Console.Write("Type in a Password: ");
0366:             string tPassword = Console.ReadLine();
0367:             Console.WriteLine("Password is: " + tPassword);
0368:             UsernameToken tUsernameToken = 
0369:                 new UsernameToken(tUsername, tPassword, PasswordOption.SendHashed);
0370:             Service1 tService1 = new Service1();
0371:             tService1.RequestSoapContext.Security.Tokens.Add(tUsernameToken);
0372:             try
0373:             {
0374:                 double tFahrenheit = tService1.ToFahrenheit(tCentigrade);
0375:                 Console.WriteLine("Fahrenheit value is: " + tFahrenheit);
0376:             }
0377:             catch(Exception pException)
0378:             {
0379:                 Console.WriteLine(pException.Message);
0380:             }
0381:         }
0382:     }
0383: }

Once again, the above code uses types from a subnamespace of the Microsoft.Web.Services namespace. As before, in order to get Visual Studio.NET to find these types, you will need to go to the Add Reference option of its Project menu, and then use this to add a reference to Microsoft.Web.Services.dll.

9. Using a language other than C#

10. Describing a Web Service using WSDL

A complete WSDL file is given below. Here is an explanation of some parts of the file:

  1. The service element of a WSDL file gives basic information about the Web Service such as the location of the .asmx file and details about which ports (HTTP GET, HTTP POST and/or SOAP) are supported.
  2. There is a binding element for each of the ports. A binding element describes for each method (provided by the Web Service) the way in which a request is coded and the way in which the reply is coded. So it might say the request is in SOAP and the reply is in SOAP. Or it might say the request is url-encoded and the reply is given using some XML coding.
  3. There are two message elements for each of the ports. One of the message elements is used to give the name and the type of each parameter, and the other message element is used to describe the type of the result. So an In message element might say that there is one parameter called pCentigrade that is a double and the corresponding Out message element might say that a double is returned.
A fuller description of the purpose of the various parts of a WSDL file is provided by [21].

Here is a WSDL file that could be used to describe the Web Service being offered by Service1.asmx:

0384: <?xml version="1.0" encoding="utf-8"?>
0385: <definitions 
0386:  xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" 
0387:  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" 
0388:  xmlns:s="http://www.w3.org/2001/XMLSchema" 
0389:  xmlns:s0="http://www.a.com/webservices/" 
0390:  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" 
0391:  xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/" 
0392:  xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" 
0393:  targetNamespace="http://www.a.com/webservices/" 
0394:  xmlns="http://schemas.xmlsoap.org/wsdl/">
0395:   <types>
0396:     <s:schema 
0397:       elementFormDefault="qualified" 
0398:       targetNamespace="http://www.a.com/webservices/">
0399:       <s:element name="ToFahrenheit">
0400:         <s:complexType>
0401:           <s:sequence>
0402:             <s:element minOccurs="1" maxOccurs="1" name="pCentigrade" 
0403:               type="s:double" />
0404:           </s:sequence>
0405:         </s:complexType>
0406:       </s:element>
0407:       <s:element name="ToFahrenheitResponse">
0408:         <s:complexType>
0409:           <s:sequence>
0410:             <s:element minOccurs="1" maxOccurs="1" name="ToFahrenheitResult"
0411:               type="s:double" />
0412:           </s:sequence>
0413:         </s:complexType>
0414:       </s:element>
0415:     </s:schema>
0416:   </types>
0417:   <message name="ToFahrenheitSoapIn">
0418:     <part name="parameters" element="s0:ToFahrenheit" />
0419:   </message>
0420:   <message name="ToFahrenheitSoapOut">
0421:     <part name="parameters" element="s0:ToFahrenheitResponse" />
0422:   </message>
0423:   <portType name="Service1Soap">
0424:     <operation name="ToFahrenheit">
0425:       <documentation>This converts from Centigrade to Fahrenheit.</documentation>
0426:       <input message="s0:ToFahrenheitSoapIn" />
0427:       <output message="s0:ToFahrenheitSoapOut" />
0428:     </operation>
0429:   </portType>
0430:   <binding name="Service1Soap" type="s0:Service1Soap">
0431:     <soap:binding transport="http://schemas.xmlsoap.org/soap/http" 
0432:       style="document" />
0433:     <operation name="ToFahrenheit">
0434:       <soap:operation 
0435:         soapAction="http://www.a.com/webservices/ToFahrenheit"
0436:         style="document" />
0437:       <input>
0438:         <soap:body use="literal" />
0439:       </input>
0440:       <output>
0441:         <soap:body use="literal" />
0442:       </output>
0443:     </operation>
0444:   </binding>
0445:   <service name="Service1">
0446:     <documentation>This Web Service provides temperature conversion
0447:       services.</documentation>
0448:     <port name="Service1Soap" binding="s0:Service1Soap">
0449:       <soap:address 
0450:         location="http://www.a.com/wsud/cs/ServerConvert/Service1.asmx" />
0451:     </port>
0452:   </service>
0453: </definitions>

11. Other products for Web Services

11.1. Introduction

As mentioned earlier, the .NET Framework is not the only way of providing a Web Service or accessing a Web Service. There are many other products that can be used. Here are two examples:

I have successfully installed each of the above products; used them to create Web Services; and used them to access Web Services. I have also successfully interoperated between the above products and .NET.

The next section of this document looks at how Axis can be used to access the .NET Web Service that was developed earlier in this document.

11.2. Accessing a Web Service from an Apache SOAP client

These interfaces and classes are used in the following Java program:

0456: import java.rmi.              RemoteException;         // ConsoleClient.java
0457: import javax.xml.rpc.         ServiceException;
0458: import com.a.www.webservices. Service1;
0459: import com.a.www.webservices. Service1Soap;
0460: import com.a.www.webservices. Service1Locator;
0461: public class ConsoleClient
0462: {
0463:     public static void main(String[] pArgs)
0464:             throws RemoteException, ServiceException
0465:     {
0466:         double tCentigrade = Double.parseDouble(pArgs[0]);
0467:         System.out.println("Centigrade: " + tCentigrade);
0468:         Service1 tService1 = new Service1Locator();
0469:         Service1Soap tService1Soap = tService1.getService1Soap();
0470:         double tFahrenheit = tService1Soap.toFahrenheit(tCentigrade);
0471:         System.out.println("Fahrenheit: " + tFahrenheit);
0472:     }
0473: }

12. Using Web Services provided by other sites

12.1. Accessing the Web Service provided by Amazon

12.1.1. Accessing and using Amazon's WSDL document

Amazon provide a number of web sites around the world (including www.amazon.co.uk). Many of these Amazon sites provide a Web Service.

There is information about Amazon Web Services at:

The WSDL document for accessing the Web Service provided by the Amazon sites in UK and Germany is located at:
and the WSDL document for the Amazon Web Services at its USA and Japan sites is located at:

12.1.2. Providing a console application that is a client of Amazon's Web Services

We can use one of the above WSDL documents to generate a proxy class for accessing the information stored at an Amazon site. You will get a proxy class called AmazonSearchService together with a number of supporting classes such as AsinRequest. The latter is used to generate a search for a particular product.

The following console application accesses the information stored at the www.amazon.co.uk site. An affliate of Amazon's site should set the tag field (of an AsinRequest object) to their affliate name. If you are not an Amazon affliate, you should set this field to "webservices-20". Whether or not you are an affliate, you still have to register with Amazon to use their Web Service. In the following code, my registration id has been replaced by "XXXXXXXXXXXXXX". Besides using the WSDL document that is appropriate for the particular Amazon site you wish to access, it is also necessary to set the locale field. In the following code, this field is set to uk. If your query only requires access to a small number of the available fields, you can set the type field to "lite": if instead you want all of the values, set this field to "heavy".

It seems it is part of the design of Amazon's Web Service to return null if a value is not available in time: it seems that their view is that it is better to be fast in providing some of the information rather than hanging around for all of it to become available.

0476: using AmazonSearchService = ConsoleAmazon.Proxy.AmazonSearchService;
0477: using AsinRequest         = ConsoleAmazon.Proxy.AsinRequest;
0478: using Console             = System.Console;
0479: using Details             = ConsoleAmazon.Proxy.Details;
0480: using ProductInfo         = ConsoleAmazon.Proxy.ProductInfo;
0481: namespace ConsoleAmazon
0482: {
0483:     public class Class1
0484:     {
0485:         public static void Main()
0486:         {
0487:             Console.Write("Type in an ISBN: ");
0488:             string tIsbnString = Console.ReadLine();
0489:             AmazonSearchService tAmazonSearchService = 
0490:                 new AmazonSearchService();
0491:             AsinRequest tAsinRequest = new AsinRequest();
0492:             tAsinRequest.tag = "webservices-20";
0493:             tAsinRequest.devtag = "XXXXXXXXXXXXXX";
0494:             tAsinRequest.asin = tIsbnString;
0495:             tAsinRequest.locale = "uk";
0496:             tAsinRequest.type = "heavy";
0497:             ProductInfo tProductInfo  = 
0498:                 tAmazonSearchService.AsinSearchRequest(tAsinRequest);
0499:             Details[] tDetailsArray = tProductInfo.Details;
0500:             Details tDetails = tDetailsArray[0];
0501:             Console.WriteLine(tDetails.ProductName);
0502:             Console.WriteLine(tDetails.ReleaseDate);
0503:             Console.WriteLine(tDetails.OurPrice);
0504:             Console.WriteLine(tDetails.ListPrice);
0505:             Console.WriteLine(tDetails.UsedPrice);
0506:             Console.WriteLine(tDetails.SalesRank);
0507:             Console.WriteLine(tDetails.Availability);
0508:             Console.WriteLine(tDetails.ImageUrlSmall);
0509:             Console.WriteLine(tDetails.ImageUrlMedium);
0510:             Console.WriteLine(tDetails.ImageUrlLarge);
0511:         }
0512:     }
0513: }

12.2. Sites providing Web Services

The above URL ends in .asmx?WSDL. The .asmx is a sure sign that this Web Service has been implemented using Microsoft's .NET Framework. As explained earlier, one of the benefits of using .NET for Web Services is that WWW pages that can be used to test a Web Service are dynamically generated. You can go to the first of these WWW pages by using the above URL without the last 5 characters, i.e., by going to:

You should get a WWW page saying:

0520: The following operations are supported. For a formal definition, 
0521: please review the Service Description.
0522:    * ZipToLatLong
0523:    * CityToZip
0524:    * ZipToCityAndState
0525:    * CityTo1Zip
0526:    * ZipTo1CityAndState

This is a list of the names of the methods provided by the Web Service. If you now click on one of these names, e.g., ZipTo1CityAndState, another WWW page is dynamically generated. It is the WWW page at:
This WWW page provides a textbox and an Invoke button.

If you type a zipcode like 94042 into the textbox and click on the Invoke button, the browser will contact the webserver at www.winisp.net to get it to execute the ZipTo1CityAndState method. After a little time, your browser will give you the following WWW page:

0527: <?xml version="1.0" encoding="utf-8"?>
0528: <string xmlns="http://dinoch.dyndns.org/webservices/">
0530: </string> 

You may have to click on View Page Source to see this XML.

You could use the techniques described in Section 4 of this document to provide a program that accesses this Web Service.

13. References

  1. Apache, 'Axis User's Guide', http://ws.apache.org/axis/
  2. Mike Clark, Peter Fletcher, J. Jeffrey Hanson, Romin Irani, Mark Waterhouse, Jorgen Theli, 'Web Services Business Strategies and Architectures', Expert Press, 2002, 1904284132.
  3. Barry Cornelius, 'Web Services using Axis', http://www.dur.ac.uk/barry.cornelius/papers/web.services.using.axis/
  4. Barry Cornelius, 'A Taste of C#', http://www.dur.ac.uk/barry.cornelius/papers/a.taste.of.csharp/
  5. Barry Cornelius, 'Comparing .NET with Java', http://www.dur.ac.uk/barry.cornelius/papers/comparing.dotnet.with.java/
  6. Ray Djajadinata, 'Yes, you can secure your Web services documents', http://www.javaworld.com/javaworld/jw-08-2002/jw-0823-securexml.html
  7. Chrisina Draganova, 'Web Services and Java', http://www.ics.ltsn.ac.uk/pub/jicc7/draganova2.doc
  8. Gil Hansen, 'Gil Hansen's XML & WebServices URLs', http://www.javamug.org/mainpages/XML.html#WebServices
  9. IBM and Microsoft, 'Federation of Identities in a Web Services World', http://msdn.microsoft.com/webservices/understanding/advancedwebservices/default.aspx?pull=/library/en-us/dnglobspec/html/ws-federation-strategy.asp
  10. IBM and Microsoft, 'Reliable Message Delivery in a Web Services World', ftp://www6.software.ibm.com/software/developer/library/ws-rm-exec-summary.pdf
  11. IBM, Microsoft and Verisign, 'Secure, Reliable, Transacted Web Services: Architecture and Composition', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwebsrv/html/wsoverview.asp
  12. IBM, Microsoft and Verisign, 'Web Services Security (WS-Security)', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnglobspec/html/ws-security.asp
  13. IBM, Microsoft and Verisign, 'WS-Security Addendum', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnglobspec/html/ws-security-addendum.asp
  14. Jonathan Lurie and R. Jason Belanger, 'The great debate: .Net vs. J2EE', http://www.javaworld.com/javaworld/jw-03-2002/jw-0308-j2eenet.html
  15. Anbazhagan Mani and Arun Nagarajan, 'Understanding quality of service for Web services', http://www-106.ibm.com/developerworks/library/ws-quality.html
  16. Joseph Mayo, 'C# Unleashed', SAMS, 2001, 0-672-32122-X.
  17. Klaus Michelsen, 'C# Primer Plus', SAMS, 2001, 0-672-32152-1.
  18. Microsoft, 'Visual Studio.NET and .NET Framework Reviewers Guide', http://download.microsoft.com/download/VisualStudioNET/Utility/7.0/W9X2K/EN-US/frameworkevalguide.doc
  19. Microsoft, 'Walkthrough: Creating a Web Service Using Visual Basic or C#', Help system with Visual Studio.NET.
  20. Microsoft, 'Walkthrough: Accessing a Web Service Using Visual Basic or C#', Help system with Visual Studio.NET.
  21. Microsoft, 'Web Services Description Language (WSDL) Explained', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwebsrv/html/wsdlexplained.asp
  22. Microsoft (Scott Seely), 'Understanding WS-Security', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwebsrv/html/understwspol.asp
  23. Microsoft, 'Web Services Enhancements (WSE)', http://msdn.microsoft.com/webservices/building/wse/default.aspx
  24. Microsoft, 'Web Services Enhancements (WSE) 2.0 Technology Preview', http://www.microsoft.com/downloads/details.aspx?FamilyId=21FB9B9A-C5F6-4C95-87B7-FC7AB49B3EDD&displaylang=en
  25. Microsoft (Matt Powell), 'Programming with Web Services Enhancements 2.0', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwse/html/programwse2.asp
  26. Microsoft (Don Smith), 'WS-Security Drilldown in Web Services Enhancements 2.0', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwse/html/wssecdrill.asp
  27. Microsoft (Matt Powell), 'WS-Security Authentication and Digital Signatures with Web Services Enhancements', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwse/html/wssecauthwse.asp
  28. Microsoft (Jeannine Hall Gailey), 'Encrypting SOAP Messages Using Web Services Enhancements', http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwse/html/wseencryption.asp
  29. Benjamin Mitchell, '.NET Enterprise Web Services: WSE and Indigo', http://noops.prismtechnologies.com/talks/50/NOOPSDec11_Edit.ppt
  30. Chris Peiris, 'Creating a .NET Web Service', http://www.15seconds.com/issue/010430.htm
  31. Jeffrey Richter, 'Applied Microsoft .NET Framework Programming', Microsoft Press, 2002, 0-7356-1442-9.
  32. John Sharp and Jon Jagger, 'Microsoft Visual C# .NET Step by Step', Microsoft Press, 2003, 0-7356-1909-3.
  33. Aaron Skonnard, 'Publishing/Discovering Web Services via DISCO/UDDI', http://www.develop.com/conferences/conferencedotnet/materials/W4.pdf
  34. Sun Microsystems, 'Java Web Services Developer Pack 1.1', http://java.sun.com/webservices/webservicespack.html
  35. Thuan Thai and Hoang Q. Lam, '.NET Framework Essentials (3rd edition)', O'Reilly, 2003, 0-596-00505-9.
  36. Doug Tidwell, James Snell, Pavel Kulchenko, 'Programming Web Services with SOAP', O'Reilly, 2001, 0-596-00095-2.
  37. Paul Townend, 'Web Service Security', http://www.dur.ac.uk/p.m.townend/webServiceSecurity.ppt
  38. 'uddi.org', http://www.uddi.org/
  39. Venu Vasudevan, 'A Web Services Primer', http://webservices.xml.com/pub/a/ws/2001/04/04/webservices/
  40. W3C, 'Simple Object Access Protocol (SOAP)', http://www.w3.org/2000/xp/Group/
  41. W3C, 'Web Services Activity', http://www.w3.org/2002/ws/
  42. 'WebServices.org', http://www.webservices.org/
  43. 'XMethods', http://www.xmethods.com/

Date: 6th May 2004; first created: 31st January 2002 Author: Barry Cornelius.
This page is copyrighted