Share via

This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


The Programmable Web: Web Services Provides Building Blocks for the Microsoft .NET Framework

Mary Kirtland
This article assumes you�re familiar with HTTP and XML
Level of Difficulty    1   2   3 
SUMMARY Web Services are building blocks for constructing distributed Web-based applications in a platform, object model, and multilanguage manner. Web Services are based on open Internet standards, such as HTTP and XML, and form the basis of Microsoft's vision of the programmable Web.
      This article defines Web Services and the key enabling technologies that ensure services can be aggregated into applications. It then describes Microsoft's new Microsoft .NET Framework and its support for creating and consuming Web Services.

ne of today's most pressing challenges is application integration: taking different applications running on different operating systems built with different object models using different programming languages and turning them into easy-to-use Web applications. The Microsoft vision of Web Services, based on open Web standards such as HTTP and XML, addresses this challenge.
      But supporting standard protocols isn't enough. There must be some way to create, deploy, scale, and maintain these Web Services. This is where the Microsoft® .NET Framework comes in.
      This article describes Web Services and the components of the Microsoft .NET Framework, including the common language runtime, the services framework, and the programming models for building and integrating Web Services.

A Look at Web Services

      Broadly speaking, a Web Service is simply an application delivered as a service that can be integrated with other Web Services using Internet standards. In other words, it's a URL-addressable resource that programmatically returns information to clients who want to use it. One important feature of Web Services is that clients don't need to know how a service is implemented. In this section, I'll explain how Web Services combine the best aspects of component-based technologies and the Web, and introduce the infrastructure needed to communicate with Web Services.
      Like components, Web Services represent black-box functionality that can be reused without worrying about how the service is implemented. Web Services provide well-defined interfaces, called contracts, that describe the services provided. Developers can assemble applications using a combination of remote services, local services, and custom code. For example, a company might assemble an online store using the Microsoft Passport service to authenticate users, a third-party personalization service to adapt Web pages to each user's preferences, a credit-card processing service, a sales tax service, package-tracking services from each shipping company, an in-house catalog service that connects to the company's internal inventory management applications, and a bit of custom code to make sure that their store stands out from the crowd. Figure 1 shows a model that illustrates how Web Services can be linked to create distributed Web applications.
Figure 1 Web Services Application Model
Figure 1 Web Services Application Model

      Unlike current component technologies, however, Web Services do not use object model-specific protocols such as DCOM, RMI, or IIOP that require specific, homogeneous infrastructures on both the client and service machines. While implementations tightly coupled to specific component technologies are perfectly acceptable in a controlled environment, they become impractical on the Web. As the set of participants in an integrated business process changes, and as technology changes over time, it becomes very difficult to guarantee a single, unified infrastructure among all participants. Web Services take a different approach; they communicate using ubiquitous Web protocols and data formats such as HTTP and XML. Any system supporting these Web standards will be able to support Web Services.
      Furthermore, a Web Service contract describes the services provided in terms of the messages the Web Service accepts and generates rather than how the service is implemented. By focusing solely on messages, the Web Services model is completely language, platform, and object model-agnostic. A Web Service can be implemented using the full feature set of any programming language, object model, and platform. A Web Service can be consumed by applications implemented in any language for any platform. As long as the contract that explains the service's capabilities and the message sequences and protocols it expects is honored, the implementations of Web Services and Web Service consumers can vary independently without affecting the application at the other end of the conversation.
      The minimum infrastructure required by the Web Services model is purposefully low to help ensure that Web Services can be implemented on and accessed from any platform using any technology and programming language. The key to Web Service interoperability is reliance solely on Web standards. However, simply agreeing that Web Services should be accessed through standard Web protocols is not sufficient to make it easy for applications to use Web Services. Web Services become easy to use when a Web Service and Web Service consumer rely on standard ways to represent data and commands, to represent Web Service contracts, and to figure out the capabilities a Web Service provides.
      XML is the obvious choice for defining a standard yet extensible language to represent commands and typed data. While rules for representing commands and typed data using other techniques (such as encoding as a query string) could be defined, XML is specifically designed as a standard metalanguage for describing data. The Simple Object Access Protocol (SOAP) is an industry standard for using XML to represent data and commands in an extensible way. A Web Service can choose to use SOAP to specify its message formats. You can learn more about SOAP and the SOAP Toolkit for Visual Studio® 6.0 from Rob Caron's article in the August 2000 issue of MSDN Magazine (see "Develop a Web Service: Up and Running with the SOAP Toolkit for Visual Studio").
      XML is also the enabling technology for the Web Service contracts. The Service Contract Language (SCL) is an XML grammar for documenting Web Service contracts. Since SCL is XML-based, contracts are easy for both developers and developer tools to create and interpret. A draft of the SCL specification should be available shortly. (Note that the SOAP Toolkit for Visual Studio 6.0 currently supports an earlier version of SCL, known as SDL.)
      The Disco specification will describe a standard way for service providers to publish Web Service contracts and the corresponding mechanism that lets developers or developer tools discover contract documents. A draft of the Disco specification should be available by the time you read this.
      Standards like SOAP, SCL, and Disco help developers since they do not need to understand and implement different ways to access each Web Service that they use. Even better, well-tested, high-performance infrastructure supporting these standards can be supplied by development platforms, greatly simplifying the entire development process.

The Microsoft .NET Framework

      The goal of the Microsoft .NET Framework is to make it easy to build Web applications and Web Services. Figure 2 shows the Microsoft .NET Framework architecture. Built on top of operating system services is a common language runtime that manages the needs of running code written in any modern programming language. This runtime supplies many services that help simplify code development and application deployment while also improving application reliability. The .NET Framework also includes a set of class libraries that developers can use from any programming language. Above that sit various application programming models that provide higher-level components and services targeted specifically at developing Web sites and Web Services. Now I'll describe each of these layers.
Figure 2 Microsoft .NET Framework Architecture
Figure 2 Microsoft .NET Framework Architecture

Common Language Runtime

      The runtime loads and runs code written in any runtime-aware programming language. Code that targets the runtime is called managed code. Managed code simply means that there is a defined contract of cooperation between natively executing code and the runtime itself. Responsibility for tasks like creating objects, making method calls, and so on is delegated to the runtime, which enables the runtime to provide additional services to the executing code.
      The runtime features cross-language integration, self-describing components, simple deployment and versioning, and integrated security services. Let's take a quick look at each of these features.
      The runtime makes use of a new common type system capable of expressing the semantics of most modern programming languages. The common type system defines a standard set of types and rules for creating new types. The runtime understands how to create and execute these types. Compilers and interpreters use runtime services to define types, manage objects, and make method calls instead of using tool or language-specific methods.
      The primary design goal for the type system is to enable deep multilanguage integration. Code written in one language can now inherit implementation from classes written in another language; exceptions can be thrown from code written in one language and caught in code written in another; and operations such as debugging and profiling work seamlessly regardless of the languages used to write the code. This means that developers of reusable class libraries no longer need to create versions for each programming language or compiler, and developers using class libraries are no longer limited to libraries developed for the programming language they are using.
      Self-describing components, which are now possible on the Microsoft .NET Framework, simplify development and deployment and improve system reliability. Let me explain how this works. Many services provided by the runtime are driven by metadata, information that supplements the executable code. Since all the information is stored together, the executables are referred to as self-describing components.
      A key advantage of self-describing components is that no other files are needed to use the components. Separate header files are not required for class definitions; the definitions can be obtained from the component itself by inspecting the metadata. Separate IDL files, type libraries, or proxy/stubs are not required to access a component across language or process boundaries; the necessary information is located in the component's metadata. Separate configuration information does not need to be deployed to identify developer requested service attributes. And best of all, since the metadata is generated from the source code during the compilation process and stored with the executable code, it is never out of sync with the executable.
      In addition to improvements in deploying individual components, the Microsoft .NET Framework defines an application deployment model that addresses customer issues with the complexities of application installation and DLL versioning (commonly known as "DLL Hell"). Services to support this model are provided by the runtime.
      The Microsoft .NET Framework introduces the notion of an assembly. An assembly is a group of resources and types, along with metadata about those resources and types, that is deployed as a unit. The metadata is called an assembly manifest and includes information such as a list of types and resources visible outside the assembly. The manifest also includes information about dependencies, such as the version of the assemblies used when the assembly was built. Developers can specify versioning policies to indicate whether the runtime should load the latest version of a dependent assembly installed on the system, a specific version, or the version used at build time.
      It has always been possible for multiple copies of a software component to reside on the same system. In general, however, only one of these copies can be registered with the operating system or loaded for execution. The policy for locating and loading components is global to the system. The .NET Framework common language runtime adds the infrastructure necessary to support per-application policies that govern the locating and loading of components, which is generally referred to as side-by-side deployment.
      Assemblies can be private to an application or shared by multiple applications. Multiple versions of an assembly can be deployed on a machine at the same time. Application configuration information defines where to look for assemblies, thus the runtime can load different versions of the same assembly for two different applications that are running concurrently. This eliminates issues that arise from incompatibilities between component versions, improving overall system stability. If necessary, administrators can add configuration information, such as a different versioning policy, to assemblies at deployment time, but the original information provided at build time is never lost.
      Because assemblies are self-describing, no explicit registration with the operating system is required. Application deployment can be as simple as copying files to a directory tree. (Things are slightly more complicated if unmanaged components must be installed for the application to work.) Configuration information is stored in XML files that can be edited by any text editor.
      Finally, the runtime also supplies integrated, pervasive security services to ensure that unauthorized users cannot access resources on a machine and that code cannot perform unauthorized actions. This improves overall system safety and reliability. Since the runtime is used to load code, create objects, and make method calls, the runtime can perform security checks and enforce security policy as managed code is loaded and executed.
      The Microsoft .NET Framework provides both code access security and role-based security. With code access security, developers can specify the required permissions their code needs to accomplish work. For example, code may need permission to write a file or access environment variables. This information is stored at the assembly level, along with information about the identity of the code. At load time and on method calls, the runtime verifies that the code can be granted the permissions it has asked for. If not, a security violation is reported. Policies for granting permissions, known as trust policies, are established by system administrators, and are based on evidence about the code such as who published the code and where it was obtained from, as well as the identity and requested permissions found in the assembly. Developers can also specify permissions they explicitly don't want, to prevent malicious use of their code by others. Programmatic security checks can be written if the permissions required depend on information that isn't known until runtime.
      In addition to code access security, the runtime supports role-based security. Role-based security builds on the same permissions model as code access security, except that the permissions are based on user identity rather than code identity. Roles represent categories of users and can be defined at development or deployment time. Policies for granting permissions are assigned to each defined role. At runtime, the identity of the user on whose behalf the code is running is determined. The runtime determines what roles the user is a member of and then grants permissions based on those roles.
      Before looking at programming models in the Microsoft .NET Framework, let's look at the services it provides.

The Services Framework

      As you may recall from Figure 2, on top of the common language runtime is the services framework. This framework provides classes that can be called from any modern programming language. All classes comply with a set of naming and design guidelines to further reduce the learning curve for developers.
Figure 3 Services Framework Class Libraries
Figure 3 Services Framework Class Libraries

      Some of the key class libraries in the services framework are shown in Figure 3. The framework includes a base set of class libraries that developers would expect in a standard language library, such as collections, input/output, string, and numerical classes. In addition, the base class library provides classes to access operating system services such as graphics, networking, threading, globalization, and cryptography. The services framework also includes a data access class library, as well as classes that development tools can use, such as debugging and profiling services. There isn't room in this article to discuss all the classes in detail, so I'll focus on the data access classes since most Web Services need access to data. Of course, you can find additional information about the services framework class libraries in the Microsoft .NET Framework SDK.

Data Access Services

      Nearly all Web Services need to query or update persisted data, whether in simple files, relational databases, or any other type of store. To provide data access, the services framework includes the ActiveX® Data Objects+ (ADO+) class library. As the name implies, ADO+ evolves from ADO. ADO+ is designed to provide data access services for scalable Web-based applications and services. ADO+ provides high-performance stream APIs for connected, cursor-style data access, as well as a disconnected data model more suitable for returning data to client applications, as I will explain later. Figure 4 illustrates the ADO+ architecture and shows that any dataâ€"regardless of how it is actually storedâ€"can be manipulated as XML or relational data, whichever is most appropriate for the application at a given point in time.
Figure 4 ADO+ Architecture
Figure 4 ADO+ Architecture

      Just like its native counterpart, ADO+ defines classes for connecting to, issuing commands against, and retrieving results from data stores. These classes are implemented by managed data providers. The ADO+ Connection and Command objects look nearly identical to those in ADO, and a new class called a DataReader provides the ability to retrieve results through a high-performance stream API. The DataReader is functionally equivalent to a forward-only, read-only ADO Recordset, but DataReaders are designed to minimize the number of in-memory objects created to avoid garbage collection and improve performance. The .NET Framework includes managed data providers for Microsoft SQL Serverâ„¢ and any data store that can be access via OLE DB.
      One of the major innovations of ADO+ is the introduction of the Dataset. A Dataset is an in-memory data cache providing a relational view of the data. Datasets know nothing about the source of their dataâ€"the Dataset may be created and populated programmatically or by loading data from a data store. No matter where the data comes from, it is manipulated using the same programming model and uses the same underlying cache. Developers using the .NET platform will be able to use the Dataset in place of the disconnected recordset in classic ADO.
      Managed data providers expose a DataSetCommand object for interfacing between data stores and Datasets. The DataSetCommand uses ADO+ Connections and Commands to fill Datasets from data stores, and resolve changes from the Dataset back to data stores.
      Just as DataReaders expose efficient stream access to relational data, XmlReaders expose efficient stream access to XML Data. Developers use a DataNavigator for scrolling and editing an in-memory XmlDocument. DataNavigators are functionally equivalent to the W3C Document Object Model (DOM), but are more efficient and provide an object model that maps nicely to the relational data view. DataNavigators support XPath syntax for navigating the data stream. ADO+ also provides an XMLDocument class for developers who want to continue to use the DOM as an object model for XML rather than the more efficient DataNavigator model.
      Since all data can be viewed as XML, developers can take advantage of ADO+ transformation and validation services for any data. ADO+ defines a general transformation architecture that consumes a DataNavigator and produces a new XmlReader. The .NET Framework provides a specific transformation component that supports the W3C XSL Transformations (XSLT) specification. ADO+ also provides a validation engine that uses XML Schemas to validate an XmlReader. ADO+ supports schemas defined via DTDs, XSD, or XDR.

Win Forms Application Model

      Conceptually, on top of the services framework sit two application models: the Windows® application model and the Web application model. Although I've focused on the Microsoft .NET Framework as a way to develop Web Services and Web applications, the framework can also be used to develop more traditional Windows-based applications (of course, these applications can use Web Services, too).
      Developers writing client applications for Windows can use the Win Forms application model to take advantage of all the rich user interface features of Windows, including existing ActiveX controls and new features of Windows 2000, such as transparent, layered, and floating windows. Either a traditional Windows or Web appearance can be selected. Developers will find the Win Forms programming model and design-time support very intuitive, given its similarities to existing Windows-based forms packages.
      Win Forms also takes advantage of the Microsoft .NET Framework runtime to reduce total cost of ownership for Windows-based client applications. The framework security model can safely execute applications and components on client machines provided they are written using the Win Forms model or used from a Win Forms application. If implemented or used in this way, the cute game someone got off the Internet won't corrupt configuration information and data, or automatically send e-mail to everyone in the user's address book.
      The Microsoft .NET Framework assembly model simplifies application deployment and versioning. Applications can be configured to use the versions of shared components they were built and tested with, rather than using whatever component versions happen to be installed on the client machine, improving application reliability and eliminating one of the major causes of application support calls: incompatible versions of user interface controls and other shared components.

A Model for Web Applications

      Web applications built on the Microsoft .NET Framework share a common application model. In this model, a Web application is a set of URLs rooted at some base URL. Thus it encompasses both Web applications that generate pages for display in a browser and Web Services. In this section, I'll detail the Web application programming model called Active Server Pages+ (ASP+), which is illustrated in Figure 5.
Figure 5 ASP+ Web Application Model
Figure 5 ASP+ Web Application Model

      As you might guess from the name, ASP+ evolves from Active Server Pages. ASP+ takes advantage of the common language runtime and services framework to provide a reliable, robust, scalable hosting environment for Web applications. ASP+ also benefits from the common language runtime assembly model to simplify application deployment. In addition, it provides services to simplify application development (such as state management services) and higher-level programming models (such as ASP+ Web Forms and ASP+ Web Services).
      At the core of ASP+ is the HTTP runtime, a high-performance runtime for processing HTTP requests that is based on a low-level architecture similar to the ISAPI architecture provided by Microsoft Internet Information Services (IIS). The HTTP runtime, as you saw in Figure 5, is managed code that runs within an unmanaged host process, such as IIS on server machines or Microsoft Internet Explorer on client machines. The HTTP runtime is responsible for processing all incoming HTTP requests, resolving the URL of each request to an application, and then dispatching the request to the application for further processing. The HTTP runtime is multithreaded and processes requests asynchronously, so it cannot be blocked by bad application code from processing new requests. Furthermore, the HTTP runtime assumes that failures will occur, so it is engineered to automatically recover as best it can from access violations, memory leaks, deadlocks, and so on. Barring hardware failure, the runtime aims for 100 percent availability.
      ASP+ uses the Microsoft .NET Framework deployment model based on assemblies, thus gaining all its benefits such as XCOPY deployment, side-by-side deployment of assemblies, and XML-based configuration. Another major benefit of ASP+ is support for live updating of applications. An administrator doesn't need to shut down the Web server or even the application to update application files. Application files are never locked, so they can be overwritten even when the application is running. When files are updated, the system gracefully switches over to the new version. The system detects file changes, launches a new instance of the application using the new application code, and begins routing incoming requests to that application. When all outstanding requests being processed by the existing application instance have been handled, that instance is shut down.
      Within an application, HTTP requests are routed through a pipeline of HTTP modules, ultimately to a request handler. HTTP modules and request handlers are simply managed classes that implement specific interfaces defined by ASP+. The pipeline architecture makes it very easy to add services to applications: just supply an HTTP module. For example, security, state management, and tracing are implemented as HTTP modules. Higher-level programming models, such as Web Services and Web Forms, are generally implemented as request handlers. An application can be associated with multiple request handlersâ€"one per URLâ€"but all HTTP requests are routed through the same pipeline.
      The Web is a fundamentally stateless model with no correlation between HTTP requests. This can make writing Web applications difficult, since applications usually need to maintain state across multiple requests. ASP+ enhances the state management services introduced by ASP to provide three types of state to Web applications: application, session, and user. Application state, as in ASP, is specific to an application instance and is not persisted. Session state is specific to a user session with the application. Unlike ASP session state, ASP+ session state is stored in a separate process and can even be configured to be stored on a separate machine. This makes session state usable when an application is deployed on a Web farm. User state resembles session state, but generally does not time out and is persisted. Thus user state is useful for storing user preferences and other personalization information. All the state management services are implemented as HTTP modules, so they can be added or removed from an application's pipeline easily. If additional state management services are required beyond those supplied by ASP+, they can be provided by a third-party module.
      ASP+ also provides caching services to improve performance. An output cache saves completely rendered pages, and a fragment cache stores partial pages. Classes are provided so applications, HTTP modules, and request handlers can store arbitrary objects in the cache as needed.
      Now let's take a quick look at the two higher-level programming models that build on the ASP+ programming model: ASP+ Web Forms and ASP+ Web Services.

ASP+ Web Forms

      Web Forms bring the productivity benefits of Visual Basic®-based forms to Web application development. Web Forms support traditional ASP syntax that mixes HTML content with script code, but it also promotes a more structured approach that separates application code from user interface content. Web Forms controls are being introduced to provide a mechanism for encapsulating common user interface elements. These new features enable tools to support a design-time model along the lines of Visual Basic, enabling WYSIWYG tool support for Web page layout.
      Web Forms controls are responsible for generating the user interface, typically in the form of HTML. ASP+ comes with a set of Web Forms controls that mirror the typical HTML user interface widgets (including listboxes, text boxes, and buttons), and an additional set of Web controls that are more complex (such as calendars and ad rotators). One important feature of these controls is that they can be written to adapt to client-side capabilities; the same pages can be used to target a wide range of client platforms and form factors. In other words, Web Forms controls can "sniff" the client that is hitting a form and return an appropriate user experienceâ€"maybe HTML 3.2 for a down-level browser and Dynamic HTML for Internet Explorer 5.0.
      Given the stateless communication model of the Web, one of the more complex issues facing Web application developers is responding to user interaction with Web-based interfaces. Web Forms take advantage of the ASP+ infrastructure to provide a rich set of services to help developers build interactive Web pages. The net effect of these services is to enable a component-based, event-driven programming model much like client-side forms programming for application developers. The complexities of state management across user interaction with a page are hidden by ASP+ Web Forms and Web Forms controls. Rich data-binding services are also supplied, making it very easy for application developers to display data retrieved through the data access services.
      The separation of code and content enables ASP+ pages to be dynamically compiled into managed classes for fast performance. Each incoming HTTP request is delivered to a new page instance so that developers do not need to be concerned about thread safety in their code.

ASP+ Web Services

      The ASP+ Web Services infrastructure provides a high-level programming model for building Web Services with ASP+. While not required for building a Web Service using the Web Services Platform, it provides several benefits that simplify development and uses a programming model that will be very familiar to developers who have worked with ASP or Visual Basic. Developers don't need to understand HTTP, SOAP, SCL, or any other specifications for Web Services to use this programming model. The ASP+ Web Services programming model is shown in Figure 6.
Figure 6 ASP+ Web Services
Figure 6 ASP+ Web Services

      Developers can create a Web Service with ASP+ by authoring a file with the extension .asmx and deploying it as part of a Web application. The ASMX file either contains a reference to a managed class defined elsewhere or the class definition itself. The class is derived from the WebService class supplied by ASP+. Public class methods are exposed as Web Service methods by marking them with the WebMethod attribute. These methods can be invoked by sending HTTP requests to the URL of the ASMX file. You don't need to handcraft a contract for your Web Service. ASP+ inspects the class metadata to automatically generate an SCL file when requested by the caller.
      Clients may submit service requests via SOAP, HTTP GET, and HTTP POST. Conventions are defined for encoding methods and parameters as query strings for HTTP GET and form data for HTTP POST. The HTTP GET and HTTP POST mechanisms are not as powerful as SOAP, but they enable clients that don't support SOAP to access the Web Service.
      The ASP+ Web Services model assumes a stateless service architecture. Stateless architectures are generally more scalable than stateful architectures. Each time a service request is received, a new object is created, the request is converted into a method call, and the object is destroyed once the method call returns. Services can use the ASP+ State Management services if they need to maintain state across requests. Web Services based on ASP+ run in the Web application model, so they get all the security, deployment, and other benefits of that model.
      ASP+ Web Services also supplies a tool to generate strongly typed managed proxies for any Web Service described by an SCL file. The proxy generator maps messages described in the SCL file into methods on the generated class. The proxy hides all the network and marshaling plumbing from the application code, so using the Web Service looks just like using any other managed code. The proxy will preferentially use SOAP to communicate with the Web Service, but also supports the HTTP GET and HTTP POST mechanisms, so they can be used as well.


      Web Services provide a simple, flexible, standards-based model for binding applications together over the Internet that takes advantage of existing infrastructure and applications. Web applications can be easily assembled with locally developed services and existing services, irrespective of the platform, development language, or object model used to implement any of the constituent services or applications.
      The Microsoft .NET Framework provides an application model and key enabling technologies to simplify the creation, deployment, and ongoing evolution of secure, reliable, scalable, highly available Web Services while building on existing developer skills.

For related articles see:
Develop a Web Service: Up and Running with the SOAP Toolkit for Visual Studio
For background information see:
The SOAP spec
Mary Kirtland is part of the MSDN Architecture team, where she helps create practical guidance for developers who design applications using Microsoft technologies. She is the author of Designing Component-based Applications (Microsoft Press, 1998).

From the September 2000 issue of MSDN Magazine.