ASP.NET, the next generation of the Microsoft Active Server Pages (ASP) platform, is known for the ease with which it allows you to develop Web applications. It provides a layer of abstraction that lets you focus on solving business problems instead of developing the underlying plumbing, which can greatly increase your productivity. This model has been extended beyond Web Forms to include Web services.
ASP.NET is also popular because it offers a rich set of services that you can leverage when you build applications. With the introduction of ASP.NET, the platform facilitates the rapid creation and consumption of Web services. ASP.NET abstracts the underlying Web services protocols such as SOAP, WSDL, and HTTP away from the developer. As I demonstrated in Chapter 1, Web services that expose simple interfaces require little, if any, knowledge of the underlying protocols.
Sample Chapter from the book:
Building XML Web Services for the Microsoft® .NET Platform
Sometimes you need to exercise a high degree of control over the serialization of the SOAP messages and the format of the WSDL document used to describe the Web service. Fortunately, ASP.NET provides the necessary hooks that allow you to control practically every aspect of the implementation of a Web service. In this chapter, I discuss the hooks ASP.NET provides as well as examples of when to use them.
Web application developers have come to rely on services provided by the ASP platform, such as state management and security. These services have been significantly improved in ASP.NET and can be leveraged to create robust Web services. Additional services such as automatic generation of documentation have also been introduced specifically for Web services.
For a version 1 product, ASP.NET is a remarkably feature-rich and solid development platform. However, as with any V1 product, ASP.NET has some quirks. In this chapter, I talk about many of them and show you how to work through them.
Creating an ASP.NET Web Service
Let’s say that an online brokerage firm wants to provide a Web service to its customers. It could accomplish this by writing an ASP.NET Web application. However, the firm wants to extend the reach of its services so that they can be leveraged from other applications. For example, a portal site such as MSN or Yahoo! might want to provide these services but might lack the expertise or the desire to take on the burden of building the services themselves.
Instead, the portal site can provide a UI to the customer and use the brokerage firm’s Web service as the back end. At worst, the portal will retain the customer within its site and potentially increase its ad revenue. At best, the portal can charge an incremental amount on top of the fees provided by the brokerage firm. Either way, it is potentially a win-win situation for the portal company and the brokerage firm.
In this chapter, I build the Securities Web service, which allows the client to perform actions such as obtaining a quote for a particular stock, bond, or mutual fund. The individual methods will contain skeleton implementations that let you focus on the mechanics of building Web services using ASP.NET.
The first thing I need to do is define the endpoint for the Securities Web service. A Web service is defined by an .asmx file, which serves as the endpoint for the Web service. Calls made to .asmx files are intercepted and processed by the ASP.NET runtime.
The implementation of the Web service is encapsulated within a class. The class definition can either appear inline within the .asmx file or be contained in a separate dynamic link library (DLL). The .asmx page needs to contain information that the runtime can use to locate the class.
Each .asmx page contains a directive at the top of the page that specifies where and in what form the implementation of the Web service can be found. This directive is used by the ASP.NET runtime to bind the Web service to a class that contains the implementation.
Here is an example in which the implementation of the Web service is contained within the .asmx file:
<%@ WebService Language="c#" Class="BrokerageFirm.Securities" %>
// Inline definition of the Securities class
public class Securities
The Class attribute contains the fully qualified name of the class that implements the Web service. If the code resides within the .asmx file, you must set the Language attribute, which specifies the language in which the code was written.
The first time the Web service is accessed, the ASP.NET runtime will use the Language attribute to compile the code with the appropriate compiler. Thus, even if the code implementing the Web service is contained within the .asmx file, it will always be executed as compiled machine code.
Out of the box, ASP.NET is configured to dynamically compile code written in C#, Visual Basic, Visual Basic .NET, and JScript .NET. You can configure additional languages within the web.config file or the machine.config file. The following is the compilation section of the machine.config file found in the C:\WINNT\Microsoft.NET\Framework\version\CONFIG directory:
<!-- compilation Attributes:
batchTimeout="timeout in seconds"
maxBatchSize="max number of pages per batched compilation"
numRecompilesBeforeAppRestart="max number of recompilations
before appdomain is cycled"
defaultLanguage="name of a language as specified
in a <compiler/> tag below"
<compilation debug="false" explicit="true" defaultLanguage="vb">
<compiler language="c#;cs;csharp" extension=".cs"
<compiler language="vb;visualbasic;vbscript" extension=".vb"
type="Microsoft.JScript.JScriptCodeProvider, Microsoft.JScript" />
<add assembly="System, Version=1.0.xxxx.0,
<add assembly="System.Web, Version=1.0.xxxx.0,
<add assembly="System.Data, Version=1.0.xxxx.0,
<add assembly="System.Web.Services, Version=1.0.xxxx.0,
<add assembly="System.Xml, Version=1.0.xxxx.0,
<add assembly="System.Drawing, Version=1.0.xxxx.0,
As you can see, the default language is Visual Basic .NET (vb), so my C# example must set the Language attribute to c#, cs, or csharp.
The compilation section also includes a list of assemblies that are referenced by code within the .asmx file. If the Securities Web service were to reference entities from an assembly other than those listed in the preceding code, I could add a new machine-wide reference to my machine.config file or an application-wide reference to my web.config file.
The last add element specifies a wildcard for the assembly name. If an assembly is referenced within an .asmx file that was not previously listed, the ASP.NET runtime will search for the assembly. (See the product documentation for the exact search order.)
The class implementing the Web service can also reside within a compiled assembly. By convention, the assembly is placed in the Web application’s bin directory because this directory is always included in the search path of the runtime. This is the default configuration for Web services created using Visual Studio .NET. The following is the WebService directive that Visual Studio .NET creates automatically:
<%@ WebService Language="c#" Codebehind="Service1.asmx. cs" Class="BrokerageFirm.Service1" %>
As is typical in the Visual Studio product line, most of the attributes defined in the WebService directive are used by the editor and are ignored by the runtime. As I mentioned, you must specify the Language attribute only if the implementation of the Web service resides within the .asmx file. In addition, the code-behind file is always ignored by the ASP.NET runtime and is used by Visual Studio .NET to bring up the appropriate source code file when you select View Code within the IDE.
One other potential gotcha is that Visual Studio .NET will only partially maintain this file. When you rename the .asmx file to something more meaningful, Visual Studio .NET will automatically rename the associated code-behind file and update the WebService directive accordingly. As a common practice, I also rename the class that implements the Web service to match the name of the .asmx file.
Unfortunately, Visual Studio .NET will not automatically update the Class attribute. If you rename the class, you have to manually update this attribute yourself. Furthermore, double-clicking the .asmx file to update this attribute will display the design surface, not the file text. Visual Studio .NET does not provide the same buttons shown on an .aspx file’s design surface that allow you to switch between the design view and the underlying text of the file. You have to right-click the file, choose Open With, and then select Source Code (Text) Editor.
Now that I have discussed the two optionsplacing the implementation for your Web service within the .asmx file or within its own assemblyI am sure you are wondering which one you should use. Well, as with most design decisions, it depends. Placing the code within the .asmx file provides the simplest means of deployment because ASP.NET will compile the code dynamically for you. However, deploying the implementation within an assembly ensures that your code will not contain compilation errors. Also, if you are deploying the Web service outside the confines of your data center, others will not have access to the source code.
Another potential advantage of having the implementation of your Web service reside within an assembly is that it can be directly referenced by other applications hosted on the same machine. For example, suppose I provide an HTML-based UI that allows my customers access to the functionality of the Securities Web service. If the class containing the implementation of the Web service is in its own assembly, the Web application can reference the assembly and directly access the Web service class. This avoids the unnecessary overhead of accessing the functionality remotely.
You should take this approach with caution, however. Some Web services rely on services provided by the ASP.NET runtime to function correctly. For example, a Web method might set an attribute stating that the ASP.NET runtime must create a new transaction on its behalf. Because ASP.NET is responsible for ensuring that a transaction is created, no transaction will be created if the class implementing the Web service is accessed directly.
Regardless of which option you choose, the code for implementing the Web service will be the same. For starters, I will implement one method within my Securities Web service, InstantQuote. Plenty of services on the Web give quotes on the price of a company’s stock. However, these quotes are often time delayed and can be more than 20 minutes old. InstantQuote will use an extremely complex algorithm to obtain the price a security is trading at on the floor. Following is the implementation.
public class Securities : WebService
public double InstantQuote(string symbol)
double price = 0;
price = 197.75;
price = 2.50;
price = 2.25;
All right, so the algorithm is not that complex. What do you expect with an example? The important thing to note is that the implementation of the Web service is a standard public class declaration with a WebMethod attribute decorating the InstantQuote method. This class declaration can be either compiled into an assembly or placed as is within the .asmx file, and it is the same whether it is contained within the .asmx file or compiled into a separate DLL.
Each method that is intended to be exposed by the Web service must be public and must be decorated with the WebMethod attribute. This tells the ASP.NET runtime to expose the method as publicly accessible. From this point on, I will refer to a method of a class decorated with the WebMethod attribute as a Web method.
When you decorate a method with the WebMethod attribute, you can also set various properties that modify the behavior of the ASP.NET runtime. Table 6-1 lists the properties exposed by the WebMethod attribute.
Table 6-1 Properties of the WebMethod Attribute
|BufferResponse||Specifies whether the response to the client should be buffered.
|CacheDuration||Specifies the amount of time, in seconds, that a response will be cached in memory by the Web server for a given response. The default is 0.
|Description||Specifies the value of the description element under each operation element within each type definition within the ASP.NET-generated WSDL document.
|EnableSession||Specifies whether the ASP.NET session state services will be available for the implementation of the method.
|MessageName||Specifies the name of the method exposed by the Web
service. Specifically, it sets the name of the element within the body of the SOAP message that contains the parameters as well as the suffix of the SOAP action. It also specifies the prefix of the names of the message, input, and output elements within the ASP.NET-generated WSDL document.
|TransactionOption||Specifies the transactional support that should be provided for the implementation of the method. The method can serve only as the root of a transaction and cannot participate in the caller’s transaction.
The ASP.NET page framework also provides the WebService attribute. This attribute is set at the class level and is used to modify properties of the Web service as a whole. Changes made via the WebService attribute will be reflected in the Web service’s WSDL document. Table 6-2 lists the properties exposed by the WebService attribute.
Table 6-2 Properties of the WebService Attribute
|Description||Specifies the description element under the service element within the ASP.NET-generated WSDL document.
|Name||Specifies the name of the service element within the ASP.NET-generated WSDL document. It also specifies the prefix for the names of the portType, binding, and port elements.
|Namespa ce||Specifies the target namespace for the WSDL document as well as the schema document that defines the structures for encoding the parameters within the body of a SOAP message. It also specifies the prefix of the namespace for the schema that contains any custom types defined by the Web service and the value of the SOAP action.
Now that I have defined the Securities Web service, let’s talk about how clients can access it.
Transport Protocols and Bindings
The Securities Web service can be accessed by the client only over HTTP because HTTP is the only transport protocol supported by ASP.NET. However, by default the Securities Web service supports three styles of binding to the HTTP protocol: SOAP, HTTP GET, and HTTP POST.
All ASP.NET Web services support the SOAP binding. Of the three binding styles, SOAP is most often preferred because data contained within the messages is strongly typed using XML Schema. In addition, XML datatypes can be mapped fairly well to .NET datatypes.
Support for the HTTP GET/POST bindings is more limited than for SOAP. Some factors that limit the ability of the ASP.NET runtime to support the HTTP GET/POST bindings are the following:
- Required SOAP headers The HTTP GET/POST bindings do not provide a means of sending and receiving header information. If a Web service’s WSDL document states that a header must always be included in a message exchanged between the client and the server, the message must be encoded using SOAP.
- Complex input parameters ASP.NET does not support encoding complex types encoded within the name/value pair on the query string or in the body of the HTTP request.
- Multiple parameters returned to the client Only the return parameter can be passed back to the client. ASP.NET does not support encoding in/out or out parameters within the message returned to the client as a result of an HTTP GET/ POST request.
If the Web service exposes relatively simple interfaces, it can also be exposed via HTTP GET and HTTP POST. These bindings are simpler than SOAP, so they might make it easier for developers using less-sophisticated toolsets to interface with the Web service.
For example, it would be relatively straightforward to interface with the Securities Web service using the XML Document Object Model (DOM). To get the current price for Microsoft stock, you load the DOM with the results of the Web method call by passing http://localhost/Calculator.asmx/InstantQuote?symbol=MSFT to the load method. The DOM will be initialized with the following XML returned from the Web service:
<?xml version="1.0" encoding="utf-8" ?>
Once the XML DOM has been initialized, you can navigate the DOM to obtain the value of the double element.
You can easily specify which protocol bindings your Web service will support. ASP.NET provides a flexible means of configuring Web applications via a hierarchical structure of XML configuration files. The machine-wide configuration file is located at C:\WINNT\Microsoft.NET\Framework\version\CONFIG\machine.config. The machine.config file contains the default configuration for all Web applications on the machine.
A web.config file, which you can optionally create within the root directory of the Web application, extends or overrides the configuration settings within machine.config. You can also place a web.config file within a subdirectory of the Web application to extend or override the configuration settings within the Web application’s web.config file.
By default, the machine.config file is configured to support all three protocols. You can modify the machine.config or web.config file for the particular application to disable any one of the three bindings. For example, you can add the following webServices section to your web.config file to disable HTTP POST and HTTP GET:
<!-- Portions of the configuration file removed for clarity -->
The protocols added to the machine.config file by default are HttpSoap, HttpPost, HttpGet, and Documentation. I discuss Documentation in the next section. Unfortunately, in this version of ASP.NET the supported protocols are not extensible.
Valid child elements for the protocols element are add, remove, and clear. The add and remove elements add and remove a particular protocol specified by the name attribute, respectively. The clear element clears all settings that are inherited from parent configuration files. For example, the following configuration file ensures that only HttpSoap and Documentation are supported, regardless of what was set in parent configuration files:
<!-- Portions of the configuration file removed for clarity -->
First I clear any configuration settings that might have been set by a parent configuration file. Then I explicitly add HttpSoap and Documentation to the list of protocols supported by the Web service.