In Focus

Tutorial: Introduction to Web Services

A tutorial explains basics of web services followed by sample example.


Introduction

A web service allows a site to expose programmatic functionality via the Internet. Web services can accept messages and optionally return replies to those messages.

More About Web Services

Today's sites already expose functionality that allows you to do things such as query a database, book an airline reservation, check the status of an order, etc, but there is no consistent model for allowing you to program against these sites. Web Services can be invoked via HTTP-POST, HTTP-GET and the Simple Object Access Protocol (SOAP). SOAP is a remote procedure call protocol and specification developed by a group of companies including Microsoft and DevelopMentor. SOAP is based on broadly adopted Internet standards such as XML and typically runs over HTTP

For more information on SOAP please see the SOAP specification  on MSDN. Visual Studio.NET will be the formal shipping vehicle for creating rich web services on the .Net platform. With the release of Visual Studio.NET(http://msdn.microsoft.com/vstudio/nextgen/default.asp) you will be able to create web services using ASP.NET and any language that targets the common-language runtime (CLR) or ATL Server and unmanaged C++. ATL Server is a collection of ATL (Active Template Library) classes that aid the C++ developer in writing native, unmanaged ISAPI extensions and filters.

Modules and Handlers

Instead of the .aspx file extension of an ASP.NET Web page, web services are saved in files with the extension of .asmx. Whenever the ASP.NET runtime receives a request for a file with an .asmx extension, the runtime dispatches the call to the web service handler. This mapping is established in the <httphandlers>section of the config.web files for the machine and individual applications. Config.web files are human-readable, XML files that are used to configure almost every aspect of your web applications.

Handlers are instances of classes that implement the System.Web.IHTTPHandler interface. The IHTTPHandler interface defines two methods, IsReusable and ProcessRequest. The IsReusable method allows an instance of IHTTPHandler to indicate whether it can be recycled and used for another request. The ProcessRequest method takes an HttpContext object as a parameter and is where the developer of a HTTP handler begins to do his work. A particular handler ultimately services inbound requests that are received by the ASP.NET runtime. After a handler is developed, it is configured in the config.web file of the application. A typical config.web file for a machine will have lines similar to the ones below:

<httphandlers> < add verb="*" path="*.asmx" type="System.Web.Services.Protocols.WebServiceHandlerFactory, System.Web.Services" validate="false" />
</httphandlers>

<httphandlers>section states that for all requests (HTTP verbs such as GET, POST, PUT), if the file being requested has the extension of .asmx, create an instance of the WebServiceHandlerFactory, which lives in the System.Web.Services.dll assembly. If the administrator wanted this handler to only accept the GET verb, he would change the verb property to verb="Get".

Handlers accept requests and produce a response. When the HTTP runtime sees a request for a file with the extension of .aspx the handler that is registered to handle .aspx files is called. In the case of the default ASP.NET installation this handler will be System.Web.UI.PageHandlerFactory. This is the same way in which .asmx files are handled. For the default ASP.NET installation, web services are handled by System.Web.Services.Protocols.WebServiceHandlerFactory.

With this custom handler, ASP.NET is able to use reflection and dynamically create an HTML page describing the service's capabilities and methods. The generated HTML page also provides the user with a way in which to test the web methods within the service. Another advantage of ASP.NET is in publishing Web Service Definition Language (WSDL) contracts.

WSDL is an XML-based grammar for describing the capabilities of web services. WSDL allows a web service to be queried by potential consumers of your service - you can think of it as an XML-based type library made available via the web. For output to be generated, one must only make a HTTP request to the web service file passing in sdl in the querystring

SDL is an XML-based grammar for describing the capabilities of web services. SDL allows a web service to be queried by potential consumers of your service-you can think of it as an XML-based type-library made available via the web. For output to be generated, one must only make a HTTP request to the web service file passing in sdl in the querystring

(e.g. http://locahost/services/myservice.asmx ). Another nice aspect of the web service handler is that it creates a simple test web page for your services. This test page allows you to confirm that everything is working with your web service without having to write your own test client.

Now lets take a look at an example.

First Web Service

The code below is an actual working web service:

<%@ WebService Language="C#" class="GreetingService" %>
using System;
using System.Web.Services;
public class GreetingService
{
[WebMethod]
public string SayHello(string Person)
{
return "Hello, " + Person;
}
}

Returning Complex Types

Our greeting service is only returning a string. Strings as well as most numbers are considered simple types. Web services are not limited to these simple types for return values or inbound parameters. Our next web service will demonstrate how to return a complex type. For this we will create a web service that returns the date and time from the server. We could just return the date and time within a string type and force the caller to parse the string, but this wouldn't be ideal. Instead, we are going to create a LocalTime struct that will be returned to the caller. For those people that may be unfamiliar with structs, they are synonymous with VB's user-defined types (UDT). A walkthrough of the code shows us defining a LocalTime struct that will contain all of the date and time information to be returned to the client. Our LocalTime struct holds seven values; Day, Month, Year, Hour, Minute, Seconds, Milliseconds, and Timezone. The struct's definition is below:

<%@ WebService Language="C#" class="TimeService" %>
using System;
using System.Web.Services;
public struct LocalTime
{
public int Day;
public int Month;
public int Year;
public int Hour;
public int Minute;
public int Seconds;
public int Milliseconds;
public string Timezone;
}
public class TimeService
{
[WebMethod]
public LocalTime GetTime()
{
LocalTime lt =
new LocalTime();
DateTime dt = DateTime.Now;
lt.Day = dt.Day;
lt.Month = dt.Month;
lt.Year = dt.Year;
lt.Hour = dt.Hour;
lt.Minute = dt.Minute;
lt.Seconds = dt.Second;
lt.Milliseconds = dt.Millisecond;
lt.Timezone = TimeZone.CurrentTimeZone.StandardName;
return lt;
}
}
 
Now how do I use the service?

When Microsoft's Visual Studio.Net ships it will take care of discovering and importing web services for programmatic use, with effort on par with adding a reference in Visual Basic. In the meantime though, the .Net team has created a console application that takes care of requesting the SDL contract of remote web services and generating a proxy to use the service. In order to use a remote web service a few things need to happen.

First you need to know where the web service resides(ex http://www.servername.com/wstest.asmx). Next you need to create a local proxy for the remote service. The proxy allows the developer to work with the remote service as though it were local to the machine. When instantiated, the proxy accepts method calls from your code as though it were the remote service object. Calls are packaged up into SOAP methods and shipped via HTTP to the remote web service. If everything goes correctly, the remote service receives the request, unwraps the envelope, does the work that you asked it to do, then returns the results in a result envelope. Once the proxy receives this returned envelope, it is unwrapped and delivered to your code as a native method call.

The Web Services Utility

The wsdl is a console application that is supplied in Microsoft's .Net SDK. The utility takes care of requesting a SDL contract from a remote web service via HTTP and generating a proxy class for you. Although the Web Services Utility uses C# as its default proxy generation language, any language (including VB and JScript) that implements the ICodeGenerator interface will work. For us to create a proxy class for accessing my web service we use the command below:

wsdl "http://localhost/services/TimeService.asmx" /out:TimeServiceProxy.cs

The /c: parameter informs the utility that I want it to create a proxy for me. The /pa: parameter is the path to the SDL contract; this can be a local path, UNC path or URI. when we run this command a .cs file will be generated. TimeServiceProxy.cs An instance of this object is what takes care of accepting method calls, packaging up calls for SOAP, invoking via HTTP and returning the results if any to the caller. Now that you have a proxy you need to compile it using the appropriate compiler depending on the language you chose. The following command assumes that the C# compiler (csc.exe) is in the system's path and that you are working in the directory where your web service's .asmx file resides.

On my system the TimeService.asmx file is located at C:\inetpub\wwwroot\Services. Since we are working with C#, the command is (this should be on one line):

csc /out:bin\TimeServiceProxy.dll /t:library /r:system.web.services.dll /r:system.xml.serialization.dll TimeServiceProxy.cs

This command creates a DLL (library) named TimeServiceProxy.dll in the C:\inetpub\wwwroot\Services\bin directory importing the resources System.Web.Services.dll and System.xml.serialization.dll. Once we have the DLL in the bin directory of our ASP.NET application we access the methods of the remote web service as though they were running locally. But this remote web service is not limited to being used only by ASP.NET. Any .Net class can now access our time web service

TimeTest Application

To demonstrate that our time web service is usable by any .Net application, I have created a simple console application in C# that prints out the time from the remote service. This application is compiled into an executable (.exe) as opposed to a library (.dll): The TimeTestApp.exe first creates a new instance of our TimeService class that lives in the bin/TimeServiceProxy.dll assembly. Then a call is made to the GetTime method of the TimeService class (ts). The returned value is stored in a local variable named lt. The lt variable is of the type LocalTime. In case it isn't obvious, I want to point out that the LocalTime object that we are now using was originally defined in our remote .asmx file. The WebServiceUtil was able to create a local definition of the LocalTime struct based on the SDL contract that was generated and returned from the Web Service handler. Next in our code, we call GetTime and then begin to simply construct a couple of local strings that contain our formatted time and date. Then we write out the results using Console.WriteLine:

using System;
class MyClass
{
static void Main()
{
TimeService ts =
new TimeService();
LocalTime lt = ts.GetTime();
string stime = lt.Hour + ":" + lt.Minute + ":" + lt.Seconds + "." +
lt.Milliseconds + " " + lt.Timezone;
string sdate = lt.Month + "/" + lt.Day + "/" + lt.Year;
Console.WriteLine("The remote date is: " + sdate);
Console.WriteLine("The remote time is: " + stime);
}
}

To compile the TimeTest application use the following command:

csc /r:system.web.services.dll /r:TimeServiceProxy.dll TimeTestApp.cs

This command will create an executable named TimeTestApp.exe in the local directory. We could have been explicit in telling the C# compiler that we wanted an executable, but the compiler creates executables (/target:exe) by default. Another item that should be noted is that although ASP.NET applications look in the bin directory within an ASP.NET application directory to resolve references, non-ASP.NET applications first look in the current directory and then in the system path to resolve assembly references.

This article is updated to RTM by Dipal Choksi.