| White Papers Home |  White Papers | Message Board |  Search |  Products |  Purchase | News |   |
![]()
Creating and using Web Services
with the .NET framework and Visual Studio.Net
by Rick Strahl
Last Updated: 03/7/2002 (updated for .NET Release)
Part 2: Passing data over .NET Web Services:
http://www.west-wind.com/presentations/dotnetwebservices/DotNetWebServicesData.asp
Source code for this article:
http://www.west-wind.com/presentations/dotnetwebservices/dotnetwebservices.zip
Over the last few months I've spent a lot of time talking about XML and Web Services and how you can build and consume them. This time we'll look at the new features in Visual Studio.NET and ASP.NET that provide a very easy mechanism for creating Web Services and more importantly the easy mechanism used to consume those same Web Services using the .NET framework's built in support for Web Services.
Web Services promise to bring information into your applications from the Internet in much the same way that browers have made information available to end users. The .Net framework introduces Web Services as an integral part of the architecture, making it very easy to create and consume these services with minimal amounts of code written. In fact, if you read Microsoft's documentation, Web Services are featured as the new component architecture in the distributed age where not only Internet exposure is handled through them but also common reusable business and application services.
The .Net framework abstracts most of the internal logic that handles the remoting details of method calls over the wire and Visual Studio .Net builds support for Web Services directly into the development environment. With all of this in place it becomes almost as easy to call a remote method as it is to call a local method. And that after all is what Web Services are about – making server side logic easily available to client applications.
Web Services 101
I’ve covered the concepts of Web Services in previous articles (SOAP Web Services - in Code #1, 2001 and Using XML for messaging in Distributes Applications in Code #1 and #2, 2000), but I want to review the most important issues here again for those of you just tuning in now.
Web Service’s mission is to provide a Remote Procedure Call (RPC) interface for client applications to call class methods on the server side. Actually, the handling interface on the server need not be a class, but in the case of .Net and COM before it classes are typically used as the implementing entity. The idea is that in order to create a Web Service, you create a class methods with standard input and output parameters and you then mark those classes and the specific methods as exposable over the Net.
Web Services are meant to expose functionality of a server to other applications. The ‘client’ applications in this case may be a Fat Client Windows app, a Fat Server Web application that runs a standard Web backend such as ASP, Cold Fusion, Web Connection etc., a browser based client application using script code, or even Java applet running in a browser on a Unix machine. As long as a client application has support for the Simple Object Access Protocol (SOAP) it can call the remote Web Service and return data for it, assuming the user is authorized.
SOAP is an important part of this process – it’s the protocol that’s responsible for routing the RPC message from the client to the server and returning the result back to the client application. SOAP is based on XML and follows a relatively simple design that’s easy to implement. SOAP’s simple protocol has contributed to its widespread support on just about any platform and development environment. You can find SOAP clients for COM (the MSSOAP Toolkit is available for Visual Studio 6 developers), .Net (obviously), Perl, Java, C++, PHP – and just about any development environment you can think of.
Web Services without .Net
.Net makes it really easy to create and consume Web Services, but currently these solutions require .Net applications on both ends of the connection and .Net and Visual Studio.Net won't ship for some time to come yet.
In the meantime you can build Web Services with the MSSOAP toolkit, which works with COM objects and can be used with any COM capable tool such as Visual Basic, Visual FoxPro and so on. The MSSOAP toolkit exposes methods of a COM object to SOAP calls, using either an ASP or ISAPI backend handler to call and execute the Web Service exposed COM Object.
If you're using Visual FoxPro you can use MSSOAP (msdn.microsoft.com/xml) with the built in support tools from within the Visual FoxPro 7.0 development environment that lets you publish Web Services directly. If you're interested in an all FoxPro based solution that doesn't rely on COM objects you can also check out the free wwSOAP tools from West Wind Technologies which provide more flexibility and source code to adapt better to changing spec changes and varying SOAP clients. You can get wwSoap from West Wind Technologies for free at: www.west-wind.com
SOAP implementations provided by vendors typically consist of two pieces: A client side Proxy that handles the SOAP message creation and result message cracking to return the result data, as well as a server piece that implements the Web Service logic. The server piece tends to be an application server that calls out to custom Web Service classes that you create and that contain the business logic of your Web Service. The server code you write essentially consists of simple methods to handle inputs and outputs via parameters and return values respectively. The logic you write in the actual method is up to you and contains any functionality that your language of choice supports. This means writing code to call your business objects or if the process is simple enough using procedural code to perform some operation. Although Web Services can expose classes, you’ll find that typically you end up creating wrapper classes for existing business objects in order to handle the specific logic required to drive your Web Service. As such you’re breaking up the business tier with a front end service (the Web Service) and a business service (your actual business objects – or if you use procedural code just that code).
One important thing to remember is that Web Services follow typical Web rules. For one they are stateless. This means that even though Web Services expose classes, they are more of a remote procedure call interface than a remote class interface. You call methods with parameters rather than storing state in properties between method calls. In fact, none of the major Web Service implementations support properties in any way. If you need to keep state you’ll have to use Web Server specific functionality such as the ASP or ASP.NET Session object to store that state and retrieve it on subsequent hits. Since Web Services use the standard Web architecture, the same tools you’ve used for HTML based browser applications can also be used in the Web Services code, although you will find that you spend very little time accessing this functionality in your Web Service code as the frameworks abstract away the need to deal with the HTTP and Web Server layer for the most part. Things that you may need to manage yourself include state and security since the Web Service architecture doesn't provide this for you. In both cases you can however take advantage of the HTTP services (Authentication, SSL for example) or the Web server features like the session object to make short work of dealing with these issues.
SOAP proxies vary widely in quality and ease of use and unfortunately, as the official SOAP spec is still under heavy construction by the standards bodies. Interoperability is not perfect, but getting better all the time. Be sure to stay up with your vendor’s latest toolkits for best compatibility. Proxies are the key to making Web Services easy to use and consume. Microsoft’s implementations in both COM and .Net provide proxy interfaces that simulate the remote object and provide you with a simple call interface that lets you create the proxy and then call methods on it the same way as you would on a local object. Visual Studio .Net takes this one step further by actually providing you a real proxy object that contains the actual methods of the remote object making it possible to even use time saving features like IntelliSense on the object.
Web Services in .Net
Creating Web Services in .Net and consuming the service either in a Windows Form application or an ASP.Net Web page is almost trivial (well, barring some nasty beta bugs at least <bg>). For the remainder of this article I’ll walk you through creating a Web Service and then creating two clients – one as a Windows Form and one as ASP.Net Web Form to consume that Web service. I’ll keep the examples really simple in this article to demonstrate how Web Services are implemented and how they behave rather than building an elaborate sample application. In the next issue I’ll dig in and build a small application that relies on Web Services to provide all of its data and application server logic needs in a more real live scenario that demonstrates the issues that you need to deal with when building a distributed application, which differs somewhat from a standalone application.
But before we get our hands dirty let’s talk about how .Net implements Web Services so you can get an idea of what happens behind all the fancy black box code that .Net provides to make Web Services so easy to use.
Behind the scenes there are three major components that make up a Web Service:
- The Web Service on the Server side
- The client application calling the Web Service via a Web Reference
- A WSDL Web Service description that describes the functionality of the Web Service
Figure 1 - .Net Web Services use WSDL files to get a type description of the Web Service which provides the detail needed to the client to create a proxy. The proxy calls the Web Service using the SOAP protocol passing parameters and returning a return value for the remote method call.
A Web Service in .Net consists of a .ASMX page that either contains a class that provides the Web Service functionality or references a specific external class that handles the logic in an external class file. Classes are standard .Net classes and the only difference is that every method that you want to expose to the Web is prefixed with a [WebMethod] attribute. Once the .ASMX page has been created the Web Service is ready for accessing over the Web. .Net provides a very useful information page about your Web Service showing all the methods and parameters along with information on how to access the Web Service over the Web. You can also use this page to test basic operation of your Web Service without calling the Web Service with a ‘real’ client. I’ll talk more about this useful sample page later.
.Net Web Services that run over HTTP can be called in 3 different ways:
- HTTP GET Operation
You can pass parameters to a Web Service by calling the ASMX page with query string parameters for the method to call and the values of simple parameters to pass.
Example: WebDemo.asmx/MethodName?Parm1=value
- HTTP POST Operation
Works the same as GET Operation except that the parameters are passed as standard URL encoded form variables. If you use a client such as wwIPStuff you can use AddPostKey() to add each parameter in the proper parameter order.
Example: WebDemo.asmx/MethodName
- SOAP
This is the proper way to call a Web Service in .Net and it’s also the way that .Net uses internally to call Web Services.
The GET and POST operations are useful if you need to call a Web Service quickly and no SOAP client is readily available. For example, in a browser based client application it may be easier to use GET and POST instead of constructing and parsing the more complex SOAP headers that are passed back and forth in a SOAP request. But with a proper SOAP client in place SOAP provides the full flexibility of the protocol, where GET and POST operations have to stick to simple inputs and outputs. Among other things that you can do with SOAP is pass complex objects and data over the wire and for these operations to work you need to use SOAP.
WSDL – a type library for a Web Service
When you create a Web Service you automatically get support for a WSDL (Web Service Description
Language) schema that describes the Web Service by accessing the .ASMX page with a querystring of WSDL:
WSDL is a relatively new standard that is used to describe a Web Service's features and how the service should be called. As SOAP standards are getting more complex and start to include specific namespace references, complex data types a type definition format was needed to provide this information to the SOAP client tools. Microsoft especially embraced WSDL early on in it SOAP toolkits and relies heavily on the WSDL description – MS SOAP clients don't work without WSDL files. The SOAP spec does not require WSDL, but the ability to have a type definition in WSDL makes it easier to call a Web Service since the client application will not have to set various namespace references manually – this information can be retrieved from the WSDL file. Use of WSDL makes it possible to make single line method calls to various service methods that would otherwise require a number of custom configuration settings.
WSDL is very new and changing rapidly, which once again means that interoperability between tools is limited at the moment. .Net Beta 2 in particular uses a very non-standard format to describe it's method parameter and return types (they are all represented as complex types) which are unreadable by most WSDL implementations to date. This will likely be addressed by standards in the future, and is never a problem when using .Net client to .Net servers.A WSDL file describes all the methods and method signatures, as well as the namespaces and the handling URL for the Web Service in an XML document. This document works very much like a type library does in COM for the client application to determine what functionality is available in the Web Service. Visual Studio.Net uses the WSDL file to create a Web Reference on the client side from your Web Service. It reads the WSDL file and based on the definitions found in the WSDL file creates a proxy class that mimics the interface of the Web Service. The resulting class is actual source code that you can look at (see Web References sidebar). Because this class is actually linked into your client project the class becomes available in IntelliSense and you can actually see the full interface of the class as you type.
The Client Application
Client applications can be any type of application from a Web backend aggregating data to display custom content to clients to a Fat Client application running Windows forms. The process of connecting a client application in Visual Studio.Net is always the same though: You set up a Web Reference, add the Web Reference namespace and then simply call the methods of the Web Service.
Behind the scenes the method call actually calls a proxy object, which invokes the remote Web Service. The proxy base class contains all the black box magic that performs the SOAP call over the wire and the proxy class simply calls work methods in the base class to do all the dirty work. The proxy reads the WSDL file to verify that the method signature and type information is correct and up to date, then creates the SOAP envelope to send to the Web Service for processing. The proxy makes the HTTP call and passes the SOAP packet off to the Web server.
A SOAP request packet traveling over the wire looks like this:
<?xml version="1.0"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2000/10/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope">
<soap:Body>
<AddNumbers xmlns="http://tempuri.org/">
<lnNumber1>6</lnNumber1>
<lnNumber2>4</lnNumber2>
</AddNumbers>
</soap:Body>
</soap:Envelope>
On the server side the .Net framework handler through ASP.NET and the .ASMX Web Service extension picks up this SOAP request and passes it off to your class for processing. The internals of .Net take care of instantiating your Web Service class, firing the constructor and then invoking the requested method, in this case AddNumbers, passing in the two provided parameters, which are properly converted into the types specified by the WSDL file.
At this point the method executes. This is were all the user processing occurs. You’d write custom method code to do your business logic required to service the functionality of the Web Service. When you’re done you return a result value as a simple return from the method.
The .Net Web Service handlers then kick in and take your return value and package it up into a SOAP response packet which looks like this:
<?xml version="1.0"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2000/10/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2000/10/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope">
<soap:Body>
<AddNumbersResponse xmlns="http://tempuri.org/">
<AddNumbersResult>10</AddNumbersResult>
</AddNumbersResponse>
</soap:Body>
</soap:Envelope>
and sends this output back over the HTTP connection to the client application proxy. The proxy now picks up the SOAP response, and unpacks the SOAP response, performs the type conversion of the XML back into the proper return type specified in the WSDL document and returns the value back to the client side.
On the client side all of this happened in a two lines of code:
using WebDemoService; // top of code
…
/// calling Web Service class WebDemo
WebDemo oService = new WebDemo();
decimal lnResult = oService.AddNumbers(6.4);
assuming the Web Reference has been configured. Not bad for code abstraction, huh?
Creating our first Web Service
Before we begin here's a word about the language and tools used for this article: I’ll be using C# to create all code in this article using VS Build 9148 which is a pre-beta 2 build. This build is significantly different than Beta 1 so most of these examples will not work with Beta 1, but they should work with Beta 2 when it ships (by the time you read this it should be out for a couple of months or so). Such is the danger of writing articles for beta software… regardless, the concepts with minor adjustments are sure to apply no matter how many changes occur to the software.
There are two ways that you can create Web Services. One is to create self contained services that use a single .ASMX page that contain both the Web Service header and the actual class code for the Web Service. Or you can create just the Web Service header and reference a class externally. Visual Studio.Net uses the latter and we’ll look at that mechanism in a minute.
To create a self contained Web Service you can use any text editor and create something like this:
<%@ WebService Language="c#" Class="FirstWebService" %>
using System;
using System.Web;
using System.Web.Services;
public class FirstWebService {
[WebMethod]
public string HelloWorld(string lcName) {
return "Hello World, " + lcName;
}
[WebMethod]
public decimal AddNumbers(decimal lnNumber1, decimal lnNumber2) {
return lnNumber1 + lnNumber2;
}
/*
[WebMethod]
public DateTime GetServerTime() {
return DateTime.Now;
} */
}
This simple Web Service implements a couple of basic methods that demonstrate the structure of a Web Service. Note that the Web Service in this .ASMX page (FirstWebService.asmx) consists of a header and the actual class code contained in the same file.
The header determines which language and class to use:
<%@ WebService Language="c#" Class="FirstWebService" %>
Web Service classes can also be declared externally using syntax like this:
<%@ WebService Language="c#" Codebehind="FirstWebService.asmx.cs"
Class="CodeService.FirstWebService" %>
In this scenario the .ASMX file only contains the above header with the class actually residing in the Codebehind specified attribute.
Using a single file automatically applies any changes you make to the Web Service code at runtime, causing the Web Service to recompile itself on the fly. With an external class using the Codebehind attribute any changes to the class require a recompile. Another attribute called SRC can also be used instead of Codebehind which allows you to use an external class and also have it compile at runtime, but it’s unclear whether this will be a shipping feature or not at this time. In general I’d suggest to use a single file if you anticipate making many changes to the Web Service, use an external file and precompile if the Web Service is fairly stable. If you use VS.Net you can only use the latter.
The rest of the .ASMX file is the actual implementation of the Web Service, which is nothing more than a class and some namespace declarations. Note that unlike most other .Net programs the code you write inside of an .ASMX is not contained in a namespace reference! The namespace is implicit and tied to the .ASMX file.
The service is implemented just as a class and each method is exposed by using the [WebMethod] attribute to tell the compiler to expose the method:
[WebMethod]
public decimal AddNumbers(decimal lnNumber1, decimal lnNumber2)
{
return lnNumber1 + lnNumber2;
}
at minimum you need to include the following namespaces:
using System;
using System.Web;
using System.Web.Services;
to gain access to the Web Service functionality. Without this the Web Service would not compile and error out.
Testing the Web Service
To check out the Web Service you can access the URL for it like this:
http://localhost/codeservice/firstwebservice.asmx
Figure 2 – The Web Service status page lets you see and test the methods that the Web Service exposes. You can also review and optionally capture the WSDL description for the service.
Figure 2 shows the status page that comes up as a result of calling the Web Service directly. This status page is very handy in checking out the functionality of the Web Service without actually creating a client application for it. Figure 3 shows the extremely useful Method test page that allows you to type in parameters and actually test the Web Service’s operation. As you might guess this is a great way to do initial debugging of your Web Services without having to worry about both client and server pieces at the same time.
Figure 3 – The method test page of a Web Service lets you run sample requests against each method of the service. This page also shows the format for the different calling mechanisms including SOAP, GET and POST operations.
When you run one of the sample methods you’re actually using the GET operation of the Web Service – all parameters are stuck on the URL query string, so testing functionality is somewhat limited to how much data you can stuff onto the query string. Submitting the request shown in Figure 3 results in a call to the following URL:
/codeservice/firstwebservice.asmx/AddNumbers?lnNumber1=10&lnNumber2=22.12
which returns an XML document result:
<?xml version="1.0" ?>
<decimal xmlns="http://tempuri.org/">32.12</decimal>
Note that the result is not SOAP encoded and the parameters where not sent up to the server via SOAP either. The GET and POST operations are entirely independent of SOAP, but can be very useful to client applications that may not have access to a SOAP client or want to avoid the overhead that SOAP introduces into remote calls. For simple operations these GET and POST operations are quite useful. For example, if you wanted to consume this data in Visual FoxPro (or any other COM based client) you could simply do:
oDOM = CREATEOBJECT("MSXML2.DomDocument")
oDOM.async = .F.
oDOM.Load("http://…/firstwebservice.asmx/AddNumbers?lnNumber1=10&lnNumber2=22.12")
? val(oDOM.SelectSingleNode("/decimal").text)
Note that values are not typed so you need to perform the type conversions yourself.
Modifying the Web Service
If you look back at the code for the Web Service notice that there is a commented out method block (using the /* and */ delimiters which are C style block comments) of code that blocks out a third method called GetServerTime(). To make this method available in our Web Service immediately all we have to do is remove the comment strings and reload our Web Service status page and voila, the new method is immediately available. .Net detects that the .ASMX file was changed and then uses the Just In Time compiler to recompile the page on the fly. You'll notice that after you've made the change to the class that it takes a while to load again as the just in time compiler fires up and compiles the page. Subsequent loads are quick again.
So what happens if you have a syntax error in your code? Since the page is compiled and the compiler pre-checks syntax and type information of everything before executing the page. If I change the AddNumbers method to:
[WebMethod]
public decimal AddNumbers(decimal lnNumber1, decimal lnNumber2)
{
return lnNumber + lnNumber2; // lnNumber is not valid
}
you get a detailed error page as shown in figure 4:
Figure 4 – A Web Service error page provides a lot of detail relating to the error that occurred.
Because the page compiles and is checked for type information error checking at the compile stage is very thorough and can provide very detailed information about what's wrong with the code before running it. Note that you can't debug self contained .ASMX pages through Visual Studio, which is why Visual Studio will create separate .ASMX and .ASMX.cs (or .vb) pages when you create a project in the IDE shell. Compiling and debugging Web Services is much easier in Visual Studio.Net so let's see how we create a Web Service in Visual Studio.
For now I'm not going to show how to call this Web Service from a client application, but rest assured that this Web Service is fully functional and can be called from any .Net client applications that supports Web References using SOAP. I'll show an example of this later on, but before we get to the client code lets see how we create a Web Service application with Visual Studio.