Programmer to ProgrammerTM  
Wrox Press Ltd  
   
  Search ASPToday Living Book ASPToday Living Book
Index Full Text
 
ASPToday Home
 
 
Home HOME
Site Map SITE MAP
Index INDEX
Full-text search SEARCH
Forum FORUM
Feedback FEEDBACK
Advertise with us ADVERTISE
Subscribe SUBSCRIBE
Bullet LOG OFF
                         
      The ASPToday Article
October 3, 2000
      Previous article -
October 2, 2000
  Next article -
October 4, 2000
 
   
   
   
Developing Web Services with ASP+ - An Introduction   Billy Anders  
by Billy Anders
 
CATEGORY:  .NET Framework  
ARTICLE TYPE: Overview Reader Comments
   
    ABSTRACT  
 
Article Rating
 
   Useful
  
   Innovative
  
   Informative
  
 96 responses

In this article Billy Anders explains how to use ASP+ to build a Web Service on the .Net platform. He explains how web


services work in the ASP+ framework, demonstrates the tools and techniques necessary, and finally walks us through the


building of two easy–to–understand services that will kick-start our own web service applications.

   
                   
    Article Discussion   Rate this article   Related Links   Index Entries  
   
 
    ARTICLE

In this article I will explain how to use ASP+ to build a Web Service on the .Net platform. I will start off by explaining how web services work in the ASP+ framework, then demonstrate the tools and techniques necessary, and finally walk you through the building of two easy–to–understand services that will get you started in building your own web services.

Exposing Services via the Web

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. 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. Also, most web sites today are designed with a human and a browser in mind. These sites assume that a person will be manually interacting with the site and therefore expose a user–interface (UI) to facilitate this. Web services are a standardized way allowing a site to expose functionality to be leveraged not by a person sitting at a browser, but by other applications instead.

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, but was designed to be transport agnostic. This means that a SOAP envelope should be just as comfortable over other transports such as SMTP. For more information on SOAP please see the SOAP specification on MSDN.

Although the SOAP Toolkit already allows you to create services to be exposed over the web, 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 you will be able to create web services using ASP+ 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. For more information on ATL Server I recommend reading the papers on the ATL Server section section of the Visual Studio.Net section of MSDN.

Modules and Handlers

Instead of the .aspx file extension of an ASP+ web page, web services are saved in files with the extension of .asmx. Whenever the ASP+ 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+ 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>

This <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" . Likewise if the handler was only to be invoked when a particular file is called, he could change the path to something like path="MyApp.Foobar" . Then only when a request comes in for /application/MyApp.FooBar will the handler be called.

ASP+ uses a pipeline architecture for all HTTP requests. This design allows zero (0) or more objects to work on every HTTP request at different stages during the request and response. A typical pipeline for an application might have modules such as logging, authentication, authorization and session state.

This modular architecture allows developers to plug in their own components that implement new, or replace existing functionality of ASP+. An example would be session state. If a developer feels that the logging module of ASP+ isn't suitable for his needs, he can add his own or replace the out–of–the–box logging ASP+ logging functionality. The same goes for all of the other modules.

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+ installation this handler will be System.Web.UI.PageHandlerFactory . This is the same way in which .asmx files are handled. For the default ASP+ installation, web services are handled by System.Web.Services.Protocols.WebServiceHandlerFactory . With this custom handler, ASP+ 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+ is in publishing Service Definition Language (SDL) contracts. 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 a look at what the SDL looks like for my simple time web service see below:

Figure 1 – SDL Contract for Time Service

For this 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://localhost/services/timeservice.asmx?sdl \t _new&WROXEMPTOKEN=1585035ZQdCmDnEoJyYkndGbqI). Although you could write this contract yourself, I think that most will agree that it is nice that ASP+ takes care of querying your object and generating it for you. 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.

Figure 2 – GreetingService HTML Page

The web service handler created this page automatically. All that I needed to do is write my web service and ASP+ takes care of generating and displaying this web page.

Our 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;
    }
}

The class exposes a Web Method called SayHello . The [WebMethod] attribute tells the .Net compiler that this method should be made available via the web. Our SayHello method takes a user's name in the form of a string and returns the greeting of Hello and the user's name; very complex code! The cool thing here is that we just implement our code like we normally would in a class file. The "work" that I had to do was add the WebService directive and Class attribute to the declaration section (top) of our .asmx file:

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

Next, since we will be using the WebMethod attribute, we need to import the namespace in which it resides:

using System.Web.Services;

After that, for each method we want to expose as a web method (a method callable via the web) we add the [WebMethod] attribute to our public method:

[WebMethod]
public string SayHello(string 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:

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;
}

The GetTime method is where the work is done. First I create a new instance of a LocalTime struct and assign it to a variable lt . Next, I assign the returned value of the Now property to a local DateTime object ( dt ). Because DateTime , as well as TimeZone , are defined as static within the .Net framework, there is no need to create an instance before calling methods on either object. After both objects ( lt and dt ) are created it is a simple matter of assigning values from the various properties on the DateTime and TimeZone objects to the instance of my LocalTime struct. Once all the property values have been assigned, we return our LocalTime object to the caller.

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;
   }
}

Browsing to http://localhost/Sevices/TimeService.asmx%20/t%20_new?WROXEMPTOKEN=1585035ZQdCmDnEoJyYkndGbqI, you should receive the following page:

Figure 3 – Output from TimeService.asmx Page

Clicking the Invoke button on the above page gives us the output below:

Figure 4 – Time Service Returned XML Document

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 (e.g. http://www.ibuyspy.com/ibuyspycs/InstantOrder.asmx%20/t%20_new?WROXEMPTOKEN=1585035ZQdCmDnEoJyYkndGbqI). 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. Cool huh? Also it should be noted that a web service does not have to return a value.

Now we know what needs to happen. The next logical question is how do we make it happen?

The Web Services Utility

The WebServiceUtil 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:

webserviceutil /c:proxy /pa:http://localhost/services/TimeService.asmx?sdl /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 this command is ran against the time service we get the output below:

//–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
/// <autogenerated>
///   This class was generated by a tool.
///   Runtime Version: 1.0.2031.0
///
///   Changes to this file may cause incorrect behavior and will be lost if 
///   the code is regenerated.
/// </autogenerated>
//–––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––

using System.Xml.Serialization;
using System.Web.Services.Protocols;
using System.Web.Services;

public class TimeService : System.Web.Services.Protocols.SoapClientProtocol {
   public TimeService() {
      this.Url = "http://localhost/services/timeservice.asmx";
   }
   
   [System.Web.Services.Protocols.SoapMethodAttribute("http://tempuri.org/GetTime"), 
   System.Xml.Serialization.XmlElementAttribute(IsNullable=false)]
   public LocalTime GetTime() {
      object[] results = this.Invoke("GetTime", new object[0]);
      return (LocalTime)(results[0]);
   }
   public System.IAsyncResult BeginGetTime(System.AsyncCallback callback, object asyncState) {
      return this.BeginInvoke("GetTime", new object[0], callback, asyncState);
   }
   public LocalTime EndGetTime(System.IAsyncResult asyncResult) {
      object[] results = this.EndInvoke(asyncResult);
      return (LocalTime)(results[0]);
   }  
}
[System.Xml.Serialization.XmlRootAttribute("result", Namespace="http://tempuri.org/", IsNullable=false)]
public class 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;  
}

This code is generated and saved in a file named after the class name of the web service – in this case 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+ 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+. 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 ):

Figure 5 – Output of TimeTestApp

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+ applications look in the bin directory within an ASP+ application directory to resolve references, non–ASP+ applications first look in the current directory and then in the system path to resolve assembly references.

Conclusion

I've just shown you how to build a web service using the publicly available .Net SDK. Although the services that we've created were simple in functionality they should provide a solid foundation for understanding how web services work and are built on the .Net platform. In addition, I hope you can see that even though Visual Studio.Net has not yet shipped you are still able to get started creating useful Web Services with the Microsoft.Net SDK. If there is interest, next time I will demonstrate how to create a custom handler and pipeline module.

 
 
   
  RATE THIS ARTICLE
  Please rate this article (1-5). Was this article...
 
 
Useful? No Yes, Very
 
Innovative? No Yes, Very
 
Informative? No Yes, Very
 
Brief Reader Comments?
Your Name:
(Optional)
 
  USEFUL LINKS
  Related Tasks:
 
 
   
  Related ASPToday Articles
   
  • ASP.NET Interception Events: An Introduction (January 26, 2001)
  • From ASP to ASP+ – Part 1 (October 23, 2000)
  •  
           
     
     
      Related Sources
     
  • SOAP Toolkit Download: http://msdn.microsoft.com/downloads/default.asp?URL=/code/sample.asp?url=/msdn-files/027/000/242/msdncompositedoc.xml
  • Visual Studio .NET: http://msdn.microsoft.com/vstudio/nextgen/default.asp
  • ATL Server: http://msdn.microsoft.com/vstudio/nextgen/technology/atldefault.asp
  • SOAP Spec: http://msdn.microsoft.com/xml/general/soapspec.asp
  • IBuySpy InstantOrder WebService: http://www.ibuyspy.com/ibuyspycs/InstantOrder.asmx
  •  
     
           
      Search the ASPToday Living Book   ASPToday Living Book
     
      Index Full Text Advanced 
     
     
           
      Index Entries in this Article
     
  • .asmx files
  •  
  • .NET Framework
  •  
  • ASP+
  •  
  • C#
  •  
  • complex types
  •  
  • complex types, returning
  •  
  • config.web file
  •  
  • console application
  •  
  • example
  •  
  • GreetingService web service
  •  
  • ICodeGenerator interface
  •  
  • IHTTPHandler interface
  •  
  • IsReusable method
  •  
  • Pipeline Structure
  •  
  • ProcessRequest method
  •  
  • returning
  •  
  • SDL
  •  
  • Service Definition Language
  •  
  • session state
  •  
  • structs
  •  
  • TimeService web service
  •  
  • user-defined types
  •  
  • using
  •  
  • web service
  •  
  • WebServiceHandlerFactory class
  •  
  • WebServiceUtil
  •  
     
     
    HOME | SITE MAP | INDEX | SEARCH | REFERENCE | FEEDBACK | ADVERTISE | SUBSCRIBE
    .NET Framework Components Data Access DNA 2000 E-commerce Performance
    Security Admin Site Design Scripting XML/Data Transfer Other Technologies

     
    ASPToday is brought to you by Wrox Press (http://www.wrox.com/). Please see our terms and conditions and privacy policy.
    ASPToday is optimised for Microsoft Internet Explorer 5 browsers.
    Please report any website problems to webmaster@asptoday.com. Copyright © 2001 Wrox Press. All Rights Reserved.