In the last ten years, nothing has had a bigger impact on software development than the World Wide Web. Browser-based applications became the norm as the world adopted this new environment. Yet the Web has also affected applications that don't use browsers through the advent of Web services. Defined by a group of related specifications, Web services provide a way for any kind of software to interact with any other kind of software using standard communication protocols.
ASP.NET Web Services allow .NET Framework applications to use this style of communication. Commonly known as ASMX, after the file extension it uses, this technology provides a straightforward way for developers to create applications that expose and invoke Web services. Given this, understanding ASMX first requires a grasp of Web services basics, described next. Web Services FundamentalsThe core technologies of Web services can be broken down into three separate areas, each addressing a particular aspect of the problem. Those areas are:
Several other Web services technologies have also been defined. To locate services, for example, developers and applications can potentially rely on Universal Description, Discovery, and Integration (UDDI). Using UDDI, providers of Web services can advertise their offerings in a standard way, allowing clients to learn what services each provider offers and letting creators of client software learn what they need to know to build those clients. UDDI is an interesting idea, but the technology isn't widely used today. Still, Microsoft includes support in Windows for creating a UDDI registry, and developers who create .NET Framework applications can use this or other UDDI implementations if they wish.
A number of extensions to SOAP have also been defined. Known collectively as the WS-* specifications, each defines how to solve a particular kind of communication problem. The WS-Security specification, for instance, lays the groundwork for secure SOAP-based communication, while WS-AtomicTransaction addresses the challenge of achieving distributed transactions via SOAP. An extension to ASP.NET Web Services known as Web Services Enhancements (WSE) provides an implementation of WS-Security and a few other WS-* specifications. A more complete implementation of these specifications (and much more) is contained in Windows Communication Foundation (WCF), described briefly at the end of this chapter.
Each of these technologies was created by groups of vendors and users working together. XML, for instance, was created by a large group working under the auspices of the World Wide Web Consortium (W3C), while WSDL was created primarily by Microsoft and IBM. SOAP comes from a group somewhere in between in size, with Microsoft, IBM, DevelopMentor, and several others playing a role. The key point to notice about the origin of each Web services technology is that none of them is a single-vendor solution. Because of this broad support, Web services based on XML, WSDL, and SOAP can be used across virtually all platforms, languages, and object models.
ASP.NET Web Services Applications: BasicsAny application that communicates via Web services must expose one or more interface definitions expressed in WSDL, and it must also allow the operations in those interfaces to be invoked using SOAP. A technology that allows creating these applications should make doing these things as natural as possible for developers. Achieving this is a primary goal of ASP.NET Web Services.
As with most distributed computing technologies, ASP.NET Web Services distinguish between clients and servers. Creating a basic server, the first thing that most developers do, is simple. Because it's part of ASP.NET, the ASP.NET Web Services technology relies on pages to implement services. As shown in Figure 7-1, the files containing those pages have an .asmx extension (rather than the .aspx extension used by browser-oriented ASP.NET applications), and they don't contain any HTML. Apart from an occasional directive, they're just code. No HTML is required because a Web service is accessed by software, not by people, so it doesn't display a user interface. Ultimately, of course, there is often some human being whose request triggered the Web service, but the interface that person uses to accomplish this is outside the scope of the Web service itself. Web services don't define GUIs, and so .asmx pages don't contain HTML. Figure 7-1. An ASP.NET Web services application is built using .asmx pages, which produce managed objects when they're accessed.
Still, the process of accessing an .asmx page is similar to what happens when an .aspx page is accessed. A client makes a request for the page via SOAP (step 1). Note that, unlike an .aspx page, the client for an .asmx page is generally an application rather than a vanilla Web browser. The named file is loaded into an app domain in the ASP.NET worker process[1], then executed (step 2). As with an .aspx page, everything in the .asmx file becomes a managed objectit's a .NET Framework application like any other. The result of executing the page is then returned to the client, again via SOAP (step 3).
To expose a method contained in an .asmx file as a Web service, all that's required is to insert the attribute WebMethod in front of the method declaration. When the file is compiled, this attribute will be stored in the metadata of the resulting assembly, like all attributes. Its presence signals to the ASP.NET infrastructure that this method should be made accessible as a Web service. To give you a sense of how simple this is to do, think back to the Compute class used in Chapter 4's language examples. An .asmx file that exposed the C# version of that class's two methods as Web services might look like this: <%@ WebService Language="c#" %> using System.Web.Services; public class Compute { [WebMethod] public int Factorial(int f) { int i; int result = 1; for (i=2; i<=f; i++) result = result * i; return result; } [WebMethod] public double SquareRoot(double s) { return System.Math.Sqrt(s); } }
The file's contents begin with a WebService directive. Similar in form to the Page directive shown in Chapter 5, this line indicates that the Web service specified in this .asmx page is written in C# and defines a class named Compute. Next appears a using statement for the namespace System.Web.Services, followed by the definition of the Compute class. This class is virtually identical to what was shown in Chapter 4. For simplicity, this version doesn't use an interface, although that's not an important distinction here. What is important is the presence of the WebMethod attribute before both of the class's methods. This attribute is defined in System.Web.Services, hence the using statement that precedes this class, and it's the only addition required to expose these methods as Web services. (Note, however, that only public methods can be marked with the WebMethod attribute.) Version 2.0 of ASP.NET Web Services also allows applying the WebMethod attribute to methods defined in an interface, then implementing that interface with a class as usual. This option clearly separates the definition of the operations this service exposes, commonly known as its contract, from the implementation of those operations. Whichever option is chosen, ASP.NET automatically generates a WSDL interface expressing the contract in a form that can be read by any Web services client on any system.
Web services applications are deployed as ASP.NET applications, just like applications built using .aspx pages. As with .aspx pages, an .asmx page is compiled when it is first accessed by a client. The resulting assembly is stored on disk and then reused until the .asmx page that produced it is modified. Changing this page results in an automatic recompile the next time the page is accessed.
When an .asmx page is accessed through a browser, ASP.NET uses reflection to learn what Web services it exposes. ASP.NET uses this knowledge to create a Web page that allows learning about these services. The Web page provides a way to invoke the services, examples of what calls to these services look like on the wire, and even a full WSDL definition of the Web services defined in this .asmx page. In reality, of course, the clients for Web services will usually be software other than a browser, but it's still nice to have this Web page generated for you, as it provides an easy way to verify that your methods are available.
Like browser-focused ASP.NET applications, ASP.NET applications that provide Web services can also use a code-behind option. In this case, the .asmx file contains just the WebService directive with a reference to the assembly that contains the class. The actual code for that class is in a separate file, just as with browser-accessible ASP.NET applications.
Servers are useless without clients. To create a client capable of invoking the Web services in some server, a developer must first create a proxy class that exposes the same methods. This proxy can be created in Visual Studio by adding something called a Web reference to a project or by specifying an .asmx file or a WSDL file. Given this information, Visual Studio will extract the information it needs to build a proxy for the desired Web service. It's also possible to create a proxy manually using wsdl.exe, a command-line tool that reads in a WSDL file and produces a proxy in the desired programming language. Once this proxy exists, the client can create an instance of that class and call its methods just like any other class. The proxy will forward each call to the destination Web service, that method will execute, and any results will be returned though the proxy.
It's also possible to invoke a Web service asynchronously. Rather than make the call and then block waiting for a response, a client can call the Web service and go about its business. When it gets the chance, the client can check to see what results, if any, the call has returned. To do this, the client uses two methods provided by the proxy along with the normal synchronous method for each Web service it supports. The first of these begins the call, passing in any parameters, and then returns control to the client. The second ends the call, returning any results that have come back. Other options are also possible, allowing the client to avoid repeatedly checking for the call to be completed.
ASP.NET Web Services Applications: Beyond the BasicsBuilding straightforward Web services such as those just shown could hardly be simpler. Adding one attribute isn't much work, and building a client proxy is also dead easy. But then, the methods in this example's Compute class don't do much either. Web services methods that do real work commonly require a bit more complexity than what's been shown so far. This section describes some of the options ASP.NET Web Services provides for creating more powerful and more useful Web services. For example, what if an ASP.NET Web Services application needs to maintain state about its client between requests? Just as with the .aspx pages of a browser application, object instances defined via an .asmx page are created, used, and then destroyed for each request. Without some outside help, a Web services application can't store any in-memory information between requests. Fortunately, this outside help is available in the form of the standard objects provided by ASP.NET for this purpose. If a class that includes Web services methods inherits from System.Web.Services.WebService, for example, code in that class can access the Session object, the Application object, and others described in Chapter 5. These objects are also available via a static property called HttpContext.Current. However they're accessed, a Web services application can use these objects to maintain state about its clients, access the User object to learn who the client is, and use other services provided by these objects.
To use the Session object, although not the others, a method within this class must also specify the EnableSession parameter in its WebMethod attribute. For example, if the Factorial method in the Compute class shown earlier were to do this, its declaration would look like this: [WebMethod(EnableSession=true)] public int Factorial(int f) { ... }
Here's another concern: What if a method exposed as a Web service needs to group the work it does into a transaction? If the method uses ADO.NET to access a DBMS, it can certainly use the services of the Connection and Transaction objects, described in Chapter 6, to demarcate transaction boundaries, just like any other ADO.NET client. But suppose the method needs to create a transaction that spans multiple DBMSs or includes work done by other components? In the .NET Framework, these services are provided by the classes in System.EnterpriseServices, as described later in this chapter. To access these services, a method exposed as a Web service can use another parameter of the WebMethod attribute called TransactionOption. If a method to transfer money between two bank accounts were exposed as a Web service, for example, that method might be declared like this: [WebMethod(TransactionOption= TransactionOption.Required)] public bool MoveMoney(int fromAccount, int toAccount, decimal amount) { ... }
When the method executes, it will automatically run within a transaction managed by code in System.EnterpriseServices. It's important to note that this transaction includes only the work done by the MoveMoney method, which might, say, add to one account and subtract from another. It does not involve the client, whose only role is to call MoveMoney. Addressing the challenge of transactions that span SOAP calls is beyond the scope of ASP.NET Web services.
Another issue that might confront the creator of a Web services method is how to deal with SOAP headers. A SOAP message can include various headers, and each of those headers can have a mustUnderstand attribute. Given that SOAP headers are allowed to contain virtually anything, ASP.NET provides quite generic support for working with them. The foundation of that support is the SoapHeader class, contained in the namespace System. Web.Services.Protocols. This class defines common properties found in a SOAP header, including a MustUnderstand property to contain the value of a header's mustUnderstand attribute. By inheriting from this class, a developer can more easily create a class that reflects the specifics of a particular SOAP header.
One more concern stems from every Web service's dependence on XML, the representation for all information sent and received via SOAP. This information, including method names and more, will belong to a default XML namespace unless an explicit alternative is provided. It's a good idea to provide this alternative since XML namespaces are used to differentiate between information carried by different services. The WebService attribute, defined in System.Web.Services, can be applied to the class, with the attribute's Namespace parameter used to specify the XML namespace. For example, if the Compute class shown earlier were provided by the fictitious QwickBank, its declaration might look like this: [WebService(Namespace= "http://www.qwickbank.com/mathinfo")] public class Compute { . . . }
ASP.NET Web Services was Microsoft's first serious attempt to implement this new technology family. Since its original design, Microsoft and the industry as a whole have learned more about how best to use Web services. Accordingly, there are aspects of ASP.NET Web Services, such as its orientation toward a remote procedure call (RPC) model, that don't necessarily reflect current thinking. Still, this technology has become a very popular approach for creating applications that communicate using SOAP. ASP.NET Web Services conforms to the Web Services Interoperability (WS-I) Organization's Basic Profile 1.1, and this ability to interoperate with software written on non-Microsoft platforms is a huge plus. It's probably fair to say that ASP.NET Web Services is the most important option in version 2.0 of the .NET Framework for creating distributed applications. |