Creating and Consuming Web Services
Robert P. LipschutzWeb Services signal a new era of lightweight distributed application development. While Web Services are not intended nor do they have the power to solve every distributed application problem, they are an easy way to create and consume services over the Internet. One of the design goals for Web Services is to allow companies and developers to share services with other companies in a simple way over the Internet.
"Web Services lower the barrier for small companies and make large companies more agile." says Mike Amundsen, President of EraServer.NET, a .NET hosting and XML Web Service provider.
And although interoperability issues are still being worked out, Web Services show strong indications of cross-platform, cross-language compatibility. In this article, we will explain how to create and consume Web Services using Microsoft Visual Studio .NET and the VB .NET language. Throughout this article, when we say Web Services, we'll typically mean Web Services created and consumed in the Microsoft environment. For our story, we used both Release Candidate 3 and the final version that was released on February 13th at the "VSLive!" event in San Francisco. This article will explain the benefits and drawbacks of Web Services, delve into the underlying technology, and provide practical how-to advice on using Web Services in the Microsoft environment. We tried to go deeper than other articles we've seen to provide answers to some of the difficult questions. In total, we think you will like using Web Services in the Visual Studio .NET IDE environment once you understand a few of its limitations. (Note that this is the first of a four-part series.)
Web Services make it easy to create applications where the consuming application sends up a simple values (e.g. city and state) to exposed methods on a server--the server does some processing, and sends back a simple value (e.g. zip code) to the client. Some applications that lend themselves nicely to Web Services include search, stock quotes, price comparison applications, travel planning, and other applications that lend themselves to request/response architectures.
Web Services have many advantages:
Based on standard protocols such as SOAP, XML, and HTTP Cross-platform and cross-language support Simple distributed application development as compared to other techniques Type safety for datatypes
In addition, Web Services built and consumed in the Microsoft .NET environment have additional advantages:
Excellent integration with Visual Studio .NET and Internet Information Server Ability to use the same development environment and framework when building client and server applications
If you are comfortable with some simple object oriented concepts like classes and methods, Web Services will be a breeze to use in developing many kinds of applications. If you want to use objects as input parameters or return values, quite common in object oriented programming, there are some limitations. Web Services are not so good at working with complex remote objects. If your goal is to create a complex object on the server side and have a client consume this object and all of its logic, you are in for some difficulties. You'd be better off designing your application differently or using a different approach. Here are some of the limitations we'll discuss:
Cannot enforce business logic on the client Cannot access read-only properties on serializable objects Cannot use non-default constructors on serializable objects Cannot serialize datatypes such as Collection or HashTable Cannot enforce logic in property getters or setters on serializable objects
To be fair, Web Services were designed to be simple, with a simple message protocol that does not lend itself to handling complex objects. Unlike other distributed object oriented approaches such as RMI or CORBA, where you get the functionality of a remote class just as you would a local class, Web Services work differently and are more limiting in some respects. These limitations come from the simplicity of the Simple Object Access Protocol (SOAP), and change the way you might write your application as compared with other distributed application approaches. One interesting way to write Web Services applications is with XML formatted documents. If the goal is to return data, then instead of thinking about returning data objects, think about returning data as XML. As said by Yasser Shohoud, President of VBWS, developers using Web Services need to start thinking in terms of messages and XML documents rather than objects and methods." This approach requires a thorough understanding of XML, but any Web Services developer interested in interoperability will need a good understanding of HTTP, XML, SOAP, and WSDL to solve the difficult problems that sometimes arise. In this article, we took the traditional approach of using objects, and then tried to apply Web Services to our thinking. We got the job done and learned some valuable lessons.
Overall, we found Web Services in Visual Studio .NET very pleasing once we got by some of our pre-conceptions. Since VB had become an object-oriented language, we half expected Web Services to provide a powerful remote object framework where remote objects could be used as if they were local. We found instead a simpler, less powerful way of doing things, that in the end, we liked. We think the trade-offs are worth it for many kinds of applications.
The term Web Services is used quite specifically and describes applications that can be easily consumed by other applications over the Internet using the SOAP specification. The idea is to provide a simple application-to-application interface just like the Web (with its HTML and HTTP specifications, and Web browsers and servers) has provided a simple human-to-application interface. Web Services will be appreciated more by developers and architects than by end-users, but the ultimate benefit will be seen by end-users that gain access to more and better services on the Web.
Let's look at a technical description of Web Services to get on some firm ground. Web Services are applications that expose some or all of their methods to other consuming applications in a well-known standard way. A consuming application can send request messages using SOAP to the exposed methods of a Web Service and expect a SOAP response message back. Each Web Service has what is known as Web Services Description Language (WSDL) that defines the kinds of messages it accepts along with the associated response messages.
The SOAP messages are handled first by IIS and then by the ASPNET_ISAPI.DLL.
For example, a weather Web Service might accept a "GetTemperature" message with a parameter of "zip code" and return a "GetTemperatureResponse" message with an integer that indicates the temperature. The VBWS site has a number of simple weather Web Services examples.
The good news for developers is that Visual Studio .NET (and other development tools) hide SOAP and WSDL under the covers making it very easy for developers to build and consume Web Services. The developer works with things developers are used to seeing - classes, methods, and properties. On the consuming side, the developer creates what is called a Web Reference to the Web Service. All of the methods and data exposed by the Web Service, as defined by the WSDL file, show up appearing to be local classes and methods in Visual Studio .NET. A Web Reference works similar to a Visual Studio .NET Reference. For example, when you add a reference (Right Click on Project | Add Reference) to the ADO database component in Visual Studio, you have access to all of the ADO classes and methods. When you add a Web Reference to a Web Service, you have access to all of its exposed classes and methods, and Visual Studio .NET's auto-completion feature (called Intellisense) works just as it does with local classes.
Although we use Microsoft's Visual Basic .NET in this article, Web Services can be created or consumed in any programming language with support for SOAP and WSDL. Other vendors such as IBM, Oracle, Sun, BEA, and Borland to name a few, have Web Services products available or initiatives underway.
Let's talk a little more about the standards, SOAP and WSDL, both of which are handled by the World Wide Web Consortium.
Applications use SOAP to communicate with Web Services. SOAP defines a simple, human-readable message format based on XML, and in the SOAP 1.1 specification, it states a simple binding to HTTP. SOAP is simple to get your hands around. It only takes a few hours to read and understand the specification. The underlying HTTP and XML specifications offer clear advantages. HTTP travels across firewalls, is understood by Web servers, and provides a very simple request/response mechanism--a perfect lightweight distributed application protocol for the Internet. XML provides a clear way to organize information in a message. Although XML is not the most efficient way to send and process data, it is a good choice for interoperability. You can check out the SOAP Version 1.1 W3C Note and the latest version of SOAP Version 1.2 Part 1: Messaging Framework Working Draft and SOAP Version 1.2 Part 2: Adjuncts on the W3C site. Visual Studio .NET supports SOAP version 1.1.
WSDL is an XML format that describes the methods and data available in a Web Service. The WSDL interface provides both human-created descriptions and XML syntax describing the Web Service and its methods. When a developer adds a Web Reference to a Web Service, Visual Studio .NET uses the WSDL file to generate a proxy class for a Web Service, and this proxy class looks just like a local class when exposed in the IDE,. To read more about the WSDL specification, check out the W3C note, Web Services Description Language (WSDL) 1.1.
Perhaps the best way to understand the capabilities and limitations of Web Services is to compare them to other techniques. First, let's compare the consumption of a Web Service to the consumption of a local object. Later, we'll compare Web Services to other distributed application techniques.
Let's look at an example of a simple class called Plane that has private members, public read-write and read-only properties, default and non-default constructors, and a public function.
Public Class Plane
'private members
Private mName As String
Private mPassengers As Integer
Private mRange As Integer
Private mEngines As Integer
Private mTailNumber As String
'read-write properties
Public Property Name() As String
Get
Return mName
End Get
Set(ByVal Value As String)
mName = Value
End Set
End Property
Public Property Passengers() As Integer
Get
Return mPassengers
End Get
Set(ByVal Value As Integer)
If Value < 100 Then
mPassengers = Value
End If
End Set
End Property
Public Property Engines() As Integer
Get
Return mEngines
End Get
Set(ByVal Value As Integer)
mEngines = Value
End Set
End Property
'read-only property
Public ReadOnly Property PlaneTailNumber()
Get
Return mTailNumber
End Get
End Property
'default constructor
Public Sub New()
End Sub
'non-default constructor, takes input parameters
Public Sub New(ByVal TailNumber As String, ByVal Passengers As Integer, ByVal Range As Integer)
mName = "Bonanza Man"
mRange = Range
mPassengers = Passengers
mEngines = 1
mTailNumber = TailNumber
End Sub
'public function
Public Shared Function CalcRange(ByVal Fuel As Integer) As Integer
'Range increase by 10 miles for each gallon of fuel
Dim Range As Integer
Range = Fuel * 10
Return Range
End Function
End Class
A piece of code local to this class could access the read-only and read-write properties, both constructors, and the public function. Of course, the private member variables are private to this class and cannot be accessed outside of it.
To use the class, a consuming application would dimension a variable of type Plane and then create a new instance of it; in our example below, we use the parameterized constructor and then display the plane's tail number.
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim aPlane As Plane
aPlane = New Plane("N84DL", 4, 200)
MessageBox.Show(aPlane.PlaneTailNumber)
End Sub
Now, let's compare what we can do local to what a Web Service can do. First off, let's take the simple route and turn this class into a Web Service. We'll soon see why this simple route is not the best route. We create a Web Service project, copy our Plane class into the service1.asmx.vb code file, and make some minor syntax adjustments. In summary, we've added an import of the System.Web.Services namespace, an inherit statement for System.Web.Services, a <WebService()> attribute in front of the class, and a <WebMethod()> attribute just before the public function.
Imports System.Web.Services
<WebService(Namespace := "http://tempuri.org/")> _
Public Class Plane
Inherits System.Web.Services.WebService
{omitted implementation details}
'public function
<WebMethod()> Public Function CalcRange(ByVal fuel As Integer) As Integer
'Range increase by 10 miles for each gallon of fuel
Dim range As Integer
Return range = fuel * 10
End Function
End Class
If we consume this Web Service, all we will have available to us is the single public function exposed by the <WebMethod()> attribute. We've lost access to the properties and the constructors. We can't get at the properties because there is no WebMethod that returns them. So how do we get them back? Well, it turns out that we can get back the read-write property, but not the read-only property.
Some of you may already be guessing the answer. You ask why we don't just make the class serializable which should give us access to the properties, right? Well, it turns out that a class can't be both a Web Service and serializable. But what we can do is split up our plane into two parts, the Web Service part with WebMethods, and a separate serializable class with properties. Then, we can have a WebMethod e.g. "GetPlaneInfo(planeTailNumber) As PlaneDetails" that returns the serializable object we'll call "PlaneDetails" with the plane's properties.
As we split our code into two classes, we could have put the "CalcRange" function implementation details in either class, using the WebMethod itself, or we can have the WebMethod call the implementation from the other class. It's really just a matter of preference. We decided to keep the implementation in PlaneDetails so we simply call this implementation from the WebMethod.
Here's our code.
Imports System.Web.Services
<WebService(Namespace:="http://tempuri.org/")> _
Public Class Plane
Inherits System.Web.Services.WebService
Web Services Designer Generated Code
<WebMethod()> Public Function CalcRange(ByVal fuel As Integer) As Integer
'Range increase by 10 miles for each gallon of fuel
Return PlaneWebService.PlaneDetails.CalcRangeImpl(fuel)
End Function
<WebMethod()> Public Function GetPlaneInfo(ByVal planeTailNumber) As PlaneDetails
'Returns the PlaneDetails serializable object
Return New PlaneDetails(planeTailNumber, 4, 250)
End Function
End Class
<Serializable()> Public Class PlaneDetails
'private members
Private mName As String
Private mPassengers As Integer
Private mRange As Integer
Private mEngines As Integer
Private mTailNumber As String
'read-write property
Public Property Name() As String
Get
Return mName
End Get
Set(ByVal Value As String)
mName = Value
End Set
End Property
Public Property Passengers() As Integer
Get
Return mPassengers
End Get
Set(ByVal Value As Integer)
If Value < 100 Then
mPassengers = Value
End If
End Set
End Property
Public Property Engines() As Integer
Get
Return mEngines
End Get
Set(ByVal Value As Integer)
mEngines = Value
End Set
End Property
'read-only property
Public ReadOnly Property PlaneTailNumber()
Get
Return mTailNumber
End Get
End Property
'default constructor
Public Sub New()
End Sub
'non-default constructor, takes input parameters
Public Sub New(ByVal planeTailNumber As String, ByVal passengers As Integer, ByVal range As Integer)
mPassengers = passengers
mRange = range
mName = "Bonanza Man"
mEngines = 1
End Sub
'public function
Public Shared Function CalcRangeImpl(ByVal fuel As Integer) As Integer
'Range increase by 10 miles for each gallon of fuel
Dim Range As Integer
Range = fuel * 10
Return Range
End Function
End Class
Notice that we now have a Web Service class called Plane that has two WebMethods, and a Serializable class PlaneDetails that includes all of the member variables, properties, constructors, and "CalcRangeImpl" method implementation. So now, a consuming application would have access to the properties in Plane Details.
Let's see how our Web Service code works. We created a simple Windows application, gave it a Web Reference to our Web Service and tried to expose the plane details:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim SrvPlane As New localhost.Plane()
Dim APlane As localhost.PlaneDetails
APlane = SrvPlane.GetPlaneInfo("N84DL")
MessageBox.Show(APlane.Name)
MessageBox.Show(APlane.Passengers)
MessageBox.Show(APlane.Engines)
End Sub
We find that we could not show the TailNumber property--it simply didn't show up on the client side because it is a read-only property. Read-only properties are not exposed to consuming clients. Again, this is a limitation of the SOAP protocol underlying Web Services. There are many cases where you would want to send down a read-only property to a consuming application. In our example, we want to show the TailNumber to identify the plane. A workaround is to make the TailNumber read-write and ensure that it is not changed by using server logic for enforcement. It's just a little gotcha in Web Services.
Another related limitation is that you can't enforce code in property getters and setters. For example, if you wanted to ensure that the Engines property equaled 1 or 2 (we have small airplanes in our application), you would normally place this logic in the Engine property setter. Since no code can be sent down to the client, this enforcement is ignored. You'll have to enforce it on the server with some server-side validation code written separately in a method.
Web Services does not provide enough power for applications where you wish to consume complex objects and enforce business logic on the client. In these cases, consider Microsoft's remoting feature. Remoting is another form of distributed programming that Microsoft has introduced in the .NET framework. Unlike web services, remoting is built on a proprietary binary Microsoft protocol. While remoting is not interoperable with other platforms and languages, in most cases it offers more functionality and performance than web services. For more information on remoting, check out the MSDN site and specifically this excellent article, "Remoting and XML Web Services in Visual Basic .NET".
Web Services come easier than other distributed application development techniques. Distributed application development has mostly been used inside companies for Enterprise Application Integration (EAI) and has historically been the domain of uber-programmers hidden deep in the recesses of big Fortune 500 companies. These hotshots use remote object technologies like Common Object Request Broker Architecture (CORBA) and Distributed Component Object Model (DCOM) or message passing products like IBM MQ Series. These methods suffer from complexity in both development and deployment and require difficult learning curves. In contrast, Web Services opens up the world of distributed programming to the rest of us.
Kollen Glynn, Vice President of Product Development at EarthConnect, a company building Web Services in the financial services space, sums it up nicely, "DCOM and CORBA were painful to use for distributed applications. All of a sudden, it's fun to program again."
Web Services are not for every need. Web Services are designed to be simple and in the process give up some power inherent in other remote object technologies. If you want to do true distributed object oriented programming, with support for distributed read-only properties, serialization of complex types, etc., you'll need to use technologies like RMI or CORBA.
That ends our introductory story on building Web Services. In the next segment, we'll get into much more detail about Web Services from the developer's standpoint. And in the third and forth segments, we'll get even more hands-on with Web Service development and deployment.
Check out PCMag.com's article, "Web Services Warm Up".
Copyright © 2004 Ziff Davis Media Inc. All Rights Reserved. Originally appearing in Dev Source.