Chapter 33. Advanced Web Services Programming

IN THIS CHAPTER

Learning the appropriate syntax for creating web services is certainly important, but it is definitely not where the majority of work with web services takes place. Creating web services that add value in loosely coupling application components hinges more on effective design and appropriate technology decisions than it does on your knowledge of web service attributes.

This chapter introduces you to some of the techniques and design patterns that are used to create web services that add value to distributed applications, such as designing with Service-Oriented Architecture (SOA) in mind, discovering information about web services dynamically at runtime, maintaining a secure environment with web services, and even how to bind Windows Forms components to data returned from web services.

Designing for Service-Oriented Architectures (SOA)

Service-Oriented Architecture (SOA) as a design pattern has been gaining a lot of momentum lately. As technology advances and provides more and more power, SOA gradually shifts from a theory about how software should be developed to a case study in how software is developed.

SOA is considered to be the evolution of object-oriented programming by some people in the industry, whereas others consider it to be completely unrelated to the goals and purposes of object-oriented programming.

Before you can grasp the true definition of SOA, you need to understand the goals that drive this architecture. Achieving loose coupling among application components is a primary goal of SOA.

Loose Coupling and Dependencies

As time goes on and companies produce larger volumes of code, the need to reuse previously created functionality instead of rewriting it from scratch becomes more important. In fact, many times the cost of rewriting functionality instead of reusing it can mean the difference between a project that gets completed and a project that never makes it out of the first design meeting.

This introduces the concept of a dependency. Developers and designers alike often group the two different kinds of dependencies together, and this prevents the creation of the best application possible. There are two different kinds of software dependencies:

  • Real dependency—A real dependency refers to functionality or services that one system consumes from another system. A system can be a small subsection of code or it can refer to an entire application. Real dependencies always exist and cannot be mitigated with coding techniques or varying architectures.
  • Artificial dependency—An artificial dependency is the set of constraints imposed by a system on a system that intends to consume functionality. Common artificial dependencies are things such as language or platform dependencies (requiring C, C++, or Linux, for example), infrastructure dependencies, and more. An artificial dependency always exists, but it can be mitigated using various techniques.

What often happens is that an artificial dependency is listed simply as a dependency or a requirement that must be fulfilled without any further discussion. This prevents the mitigation of this dependency and prevents the creation of the best application possible and maximum reuse of functionality.

What all of us strive for is the complete, or as nearly complete as possible, mitigation of the artificial dependencies within a system. This means that when examining dependencies, you need to make a clear distinction about which dependency is based purely on required functionality and which dependency is based on artificial limitations imposed on the feature consumer by the provider.

SOA as a Way of Life

SOA is an architectural pattern in which the main goal is to achieve loose coupling by reducing artificial dependencies within a system. Within SOA, there are services (also called providers) and consumers. A service is a discrete unit of work that accomplishes some task requested by a consumer. The loose coupling is achieved when the dependency that a consumer has on a specific provider is only that the provider accomplish a specific task, and that there are few or no artificial dependencies in the way. When loose coupling is achieved, the service can be consumed by any consumer that knows the agreed-upon message format to talk to the provider. Likewise, the consumer can use any provider of the same type.

In traditional object-oriented programming, the consumer and provider are very tightly coupled, and may even be part of the same class or library of classes. This is where SOA and OOP spread apart in philosophy. OOP traditionally indicates that data and the methods that process that data should be bound together, whereas SOA strives to separate the information from the processing of the information to allow one processor to perform its task on data from multiple consumers and one consumer to send its data to multiple processors without having any impact on the system.

The concept of reusable providers and consumers can become more apparent when applied to a real-world scenario. In the real world, we consume services because those services are either too costly or take too much time for us to do ourselves. In some cases, such as performing specialized tasks for which we have no aptitude, we would be completely unable to do the job ourselves, even if we had the time and money. For example, if I were to attempt to put a new roof on my house without getting the help of a professional, the results would be disastrous. This is called an “area of concern.” Some existing services simply do the job better than the consumer could; you might consider some services to be “‘experts.”

At a hardware level, a device near and dear to the hearts of many developers can be considered a service provider: the Xbox. It provides a service of playing games. You, as the consumer, supply the information (games) to the service provider. As an output, you receive entertainment (and hopefully a high score). The service provider (Xbox) can provide the game-playing service to any input that meets the specified format. The input that you use on one Xbox can be used on any other Xbox because the service provider knows the format of the data being used as input. You can say that the Xbox is loosely coupled from the consumer in that you can play your games on any Xbox, and your Xbox can play games owned by any other consumer. The only dependency on the Xbox is a real dependency in that you must use the Xbox functionality in order to play an Xbox game.

Now that you know what SOA is, what its purpose is, and how similar models appear in everyday life, let’s take a look at an example of SOA implemented in software.

In this hypothetical scenario, a company has decided to provide a unified storefront. It has purchased several smaller companies that used to sell products individually such as a bookstore, a DVD store, a CD store, and a company that sold consumer electronics online. The new, larger company wants to be able to provide all of the products of each individual child company in a unified storefront. In addition, the larger company wants to have several different sites that all share the same catalog and product fulfillment system so that they can be branded, styled, and customized for different companies.

Without SOA, creating a solution like this would be a daunting task. Even if a solution could be developed without SOA, it would be an extremely rigid and brittle solution that would break at the slightest attempt for expansion or modification because of the tight coupling and the numerous interdependencies.

Using Service-Oriented Architecture, the company decides to create a catalog service that can be used as an interface to the product catalogs of each of the individual child companies and a dispatcher service that is used to funnel requests for catalog information and features from the front-end applications to the appropriate catalog service based on the type of product. In addition, loosely coupled shipping and credit-card–charging services will be used for maximum scalability and growth potential. This allows each of the product catalog hosts to provide their catalog to any front-end store, and the front-end stores can work with any catalog. In addition, additional shipping providers, credit-card management systems, and even product catalogs can be added to the system with very little impact on existing code. Figure 33.1 illustrates this particular SOA case study.

Figure 33.1 Service-oriented architecture model—unified storefront.

Image

This is by no means the only solution to the problem described. However, it does illustrate how the use of SOA and loosely coupled systems can dramatically reduce risk normally associated with a project of this size and can create an environment that is agile and able to cope with change and expansion without requiring a lot of rework and refactoring effort on the part of developers.

Using Web Service Discovery

Loose coupling is a recurring theme whenever developing and designing web services for distributed applications. As mentioned in the preceding section, the more artificial dependencies you can remove, the better. One such dependency is hard-coded information about the web service to which a consumer connects. Using Web Services Discovery, client code can be more versatile and agile in its consumption of web services.

Web Services Discovery works on the basis of discovery documents. A discovery document contains vital information about web services contained on a web server. Discovery documents are created on servers with a .disco extension to provide location information about web services hosted by a server. In addition, you can append the ?disco argument to the end of a request for an ASP.NET web service .asmx file to retrieve a discovery document for that service.

Using discovery documents, and other directory facilities such as UDDI to provide dynamic location of web services, you can further remove artificial dependencies from your solution.

The System.Web.Services.Discovery namespace is where the .NET Framework provides tools for reading and writing discovery documents and performing other discovery-related tasks.

To see discovery in action, first create a new web service called ServiceToDiscover and just leave the default “Hello World” service there in the Service.asmx file. Add another web service called SecondService.asmx to the project. When you add the ?disco postfix to the web service URL, for example, http://localhost/ServiceToDiscover/Service.asmx?disco, you get an XML document that looks similar to this:

Image

Probably the most important part of the discovery document is the information that points to the location of the WSDL contract data. The WSDL contract is used to construct client proxies capable of communicating with the web service.

The code in Listing 33.1 shows how to use the System.Web.Services.Discovery namespace to read and process the information contained in a remote .disco file. This Windows Forms application places each contract reference and SOAP binding in separate ListView controls.

Listing 33.1 Using System.Web.Services.discovery to Process Discovery Documents

Image

Image

Figure 33.2 shows the output of this application after processing a discovery document.

Figure 33.2 Reading information from discovery documents.

Image

Using Custom SOAP Headers

SOAP is the wire format used to communicate with web services. In SOA implementations, SOAP is used in Document mode. A less-often-used standard is SOAP-RPC, which is a special form of SOAP used to serialize Remote Procedure Calls. When SOAP documents are used as input parameters to methods, the wire format becomes more flexible and prevents additional artificial dependencies that are typically created when using SOAP-RPC.

Another feature that SOAP allows for is the use of custom headers. Attached to the top of any SOAP envelope, there can be a header that contains additional information. The great thing about using .NET with SOAP headers is that the information contained in a SOAP header can be carried from one method call to the next without explicitly writing additional code. This allows for information that needs to be transmitted to multiple methods to be sent without impacting the parameter list of each method.

The ASP.NET Web Services infrastructure insulates you from the complexities of constructing and decoding the XML that forms SOAP headers and allows you to concern yourself with the data contained within them.

The first code sample illustrates how to add a SOAP header to a web service method. This is a two-step process:

  1. Create a class that derives from SoapHeader and declare an instance of that class as a public member within the web service.
  2. Use the SoapHeaderAttribute to indicate the name of the SOAP header that will be used by a given method, as well as the header direction and whether that header is required.

To see this in action, take a look at the code in Listing 33.2, which contains the source code for a web service called SoapHeaderTest.asmx.

Listing 33.2 Using SOAP Headers in a Web Service

Image

Image

One thing worth pointing out is that you can have bidirectional SOAP headers. This means that when the web service method is done executing, the current values of the SOAP header are then serialized back into the output of the web service. The client proxy takes care of the underlying work involved in reloading the headers from the SOAP envelope, making it a very seamless and easy-to-use system.

You add references to header-enabled web services in the same manner as standard web services. The only difference is that the SOAP header appears as a member of the service proxy, allowing the client code to manipulate the header before making a method call, as shown in Listing 33.3.

Listing 33.3 Programming with SOAP Headers: Client Code

Image

When you run a Windows Forms test harness application and click the button to launch the preceding code, the MessageBox output is shown in Figure 33.3.

Figure 33.3 Output from SOAP header example.

Image

SOAP headers allow the web service and client to exchange information in a very loose manner without changing the list of parameters on the service methods. As you know, any change to a web service method signature that a client isn’t aware of will cause the client to break.

Programming Secure Web Services

Even though a lot of free, public web services are available on the Internet at the moment, the majority of all web service development is still in creating web services that can only be used by a certain set of users. In other words, clients need to be able to prove their eligibility to consume the service.

Using WSE (Web Services Extensions), a downloadable tool library from Microsoft, you can take advantage of the WS-Security standard and have some extremely powerful security features. WSE is beyond the scope of this chapter and often provides more functionality than is desired for simple authentication of clients. This section shows you a very good pattern for authenticating clients in a secure way without impacting the performance of the web service.

Before taking a look at the final pattern, you should know about some of the other alternatives that usually are not chosen. The simplest (for the client) means of authenticating clients is to pass the username and password in the SOAP header to every single method call, and then each web service method would validate those credentials and refuse to perform the work if the credentials fail to verify. There are a number of problems with this pattern. The biggest is that passwords are being transmitted in clear text via XML over the Internet. To fix that problem and protect passwords, you would have to make every single method call to the web service over SSL, or hash the password using an algorithm known to the service. No matter what you do, a lot of unnecessary overhead is still incurred for every web service method. There is already a lot of natural latency that occurs as part of using web services, so adding further overhead to method calls is not a viable option.

Another option would be to use session state. The client would make a call to a web method that validates credentials. If the credentials are valid, the client has access to the rest of the service’s functionality for the remainder of the session. On the surface, this might appear to be a fine solution. However, session cookies can be faked by malicious software intercepting communications between the service consumer and service provider and session state incurs an SOA penalty on the server; SOA relies heavily on the idea that services should be as stateless as possible.

The pattern that combines the best of the previous recommendations is one that uses a concept called a token store. One service, often called Login.asmx or Security.asmx, is only accessible via SSL. It has a method for validating user credentials. If the credentials are verified, this service returns an authentication token that is really just an arbitrary string. This token often takes the form of a GUID (Globally Unique Identifier). That GUID is then passed to subsequent non-SSL calls to the real service in the SOAP header. The web method then processes the SOAP header and determines if the token is a valid token. If the token is invalid, the method throws an exception and no work is done. Figure 33.4 shows a conceptual diagram of this model.

Figure 33.4 Using the token store web services authentication model.

Image

The model illustrated in Figure 33.4 should be sufficient for most medium-security requirements. However, a problem still exists because an intruder can intercept a SOAP envelope containing a valid authentication token and reuse that token to send altered messages to the server. In this case, messages can be digitally signed using certificates that verify not only the authenticity of the sender of the message but also that the message was not tampered in transit. If your security situation requires tight constraints such as this, you should consider using WSE for your web service security needs. You can find more information about WSE at http://msdn.microsoft.com/webservices/webservices/building/wse/default.aspx. If you want to download WSE 3.0, the version of WSE that works in conjunction with ASP.NET 2.0, you can get the library at http://www.microsoft.com/downloads/details.aspx?familyid=018a09fd-3a74-43c5-8ec1-8d789091255d&displaylang=en.

Data-Binding Windows Forms to Web Services

One of the powerful new features of Windows Forms is the consolidated Data Sources panel. This panel shows you a list of available data sources that can then be dragged onto any form in the application. For example, if you have created a DataSet, that DataSet will appear in the Data Sources panel and you can then drag it onto the surface of a form, creating either a grid view or a details view.

One of the types of data sources is a web service. This section of the chapter walks you though building a web service that returns data through a web method. The schema of this data is then interpreted as a data source by the Windows Forms client, allowing that client to bind directly to data returned by a web service.

The following steps will walk you through the process of using a web service as a data source. While reading these steps, it is important to keep in mind a very important distinction: The web service itself is not the data source—only the data types returned from that service are considered data sources. In other words, a web service data source will not automatically invoke methods; it only obtains the schema from the web service and uses that schema to prepare the data source.

The following steps will take you through the process of binding to a web service:

  1. Create a new ASP.NET web service application by selecting File, then New, then Website from within Visual Studio 2005. You can call the service anything you like.
  2. Add a class called Customer to the App_Code directory within the service. The Customer class should have a few basic public string fields, such as FirstName, LastName, and CustomerID.
  3. Add a method to the service called GetAllCustomers() with the following code:

    Image

  4. Add a new Windows Forms project to the solution called WSBindingClient. Do not add a web reference to the new project at this point.
  5. Open the Data Sources pane and click the Add New Data Source button.
  6. Select Web Service as the data source type and browse the current solution for web services.
  7. Select the “Service” service from the provided list.
  8. Click the Add Reference button. You will see a confirmation dialog that indicates that all of the objects returned by the web service will be added to the Data Sources window. Confirm this by clicking Finish.
  9. Figure 33.5 shows what your Data Sources window should look like after completing step 8.

    Figure 33.5 The Data Sources window after adding a web service data source.

    Image

  10. Drag the Customer item from the Data Sources window onto the main form.
  11. Switch to the code view beneath the form and add the following two lines of code below the call to InitializeComponent(); in the form’s constructor:

    localhost.Service svc = new localhost.Service();
    customerBindingSource.DataSource = svc.GetAllCustomers();

  12. Run the application. You should see a DataGridView that contains the two rows of data provided by the web service, as shown in Figure 33.6.

Figure 33.6 A DataGridView populated by a web service.

Image

Unlike other kinds of data sources that can be automatically updated by the controls to which they are bound, you will have to write your own code to invoke the appropriate update methods on the web service because there is no way for the data source to know which methods on which web service to invoke in response to data change events. However, even with that minor bit of required coding, the ability to bind controls to data retrieved directly from web services is a powerful tool, especially for smart client applications and SOA infrastructures.

Summary

The art to building great web services has very little to do with writing code. Most of the work in building web services is in design and architecture. This chapter provided you with an overview of the goals and concepts behind Service-Oriented Architecture and how that applies to building web services. This chapter also covered a few techniques that can help to reduce artificial dependencies, such as using SOAP headers for data transmission and implicit authentication, as well as a handy shortcut for binding data in a Windows Forms control to information retrieved from a web service.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.218.9.139