CHAPTER 4

image

Understanding HTTP Web Services

In this chapter, I explain the different ways in which ASP.NET Web API can be used to deliver an HTTP web service and the kinds of clients that each arrangement best suits. I build on this foundation to describe the two broad categories of web service that you can create: simple web services (like the one I created in Chapter 2) and RESTful web services, which are more complex but are easier to maintain.

The choice between simple and RESTful web services echoes themes that run through MVC framework development: an initial investment of design and development time that is paid back through a loosely coupled system that is easier to change over time.

To explain RESTful web services, I describe the process for designing and evolving an API that allows a client to consume the service in a loosely coupled way. The result is a description of a RESTful API that may strike you as rather abstract, but don’t worry because I back this up with implementation examples in Chapter 58, as well as detailed explanations of the ASP.NET Web API features throughout this book.

Understanding ASP.NET Web API

ASP.NET Web API solves a simple problem: it creates services that deliver data from ASP.NET applications to clients over HTTP requests, known as HTTP web services. This may sound similar to the MVC framework, but the difference is that MVC usually delivers content that mixes the data with presentation instructions to the client. Figure 4-1 shows the standard arrangement of components in an MVC framework application.

9781484200865_Fig04-01.jpg

Figure 4-1. The components in an MVC application

The MVC framework view combines Razor markup with model data to generate content that can be presented to the user, typically as a combination of HTML, CSS, and JavaScript.

ASP.NET Web API also uses a controller and a model, but it doesn’t have views. Instead, it sends just the data, as illustrated in Figure 4-2.

9781484200865_Fig04-02.jpg

Figure 4-2. The components in a Web API application

Sending just the data means that presenting the data to the user becomes the responsibility of the client that made the HTTP request. There are four different types of Web API client, each of which benefits from a data-only service in a different way: single-page applications, native applications, shared-model applications, and service applications. I describe each of the client types in the sections that follow.

The decision about which model to use is driven largely by where raw data is processed and combined with presentation elements to show to the user. The presentation need not be HTML; native clients use their own UI toolkits, and service applications may not present the data to a user at all.

Understanding Single-Page Applications

Browser-based web applications can be broken into two broad categories. The first is round-trip applications, which is where every request to the server returns a complete page of HTML content. The other category, single-page applications, starts with an HTML document and uses JavaScript to make Ajax requests to the server for additional data or fragments of HTML in order to response to user interaction. In Chapter 2, I created a simple single-page application to introduce you to ASP.NET Web API.

These round-trip and single-page categories are the ends of a spectrum, and most modern web applications fall somewhere in the middle such that some requests return complete HTML documents, while others are just for data.

For most MVC framework developers, single-page applications are the reason that ASP.NET Web API is interesting, allowing an HTTP web service to be used alongside the MVC framework components of an application, and it is this model that I focus on for most of the book. The MVC framework is used to deliver the initial content, which is then supplemented or updated using Ajax requests to an ASP.NET Web API web service, as shown in Figure 4-3.

9781484200865_Fig04-03.jpg

Figure 4-3. Using MVC and Web API in a single-page application

The model data is processed in two places: in the view when the initial content for the application is requested from the MVC controller and in the browser when the data is received from the Web API controller. ASP.NET makes it easy to create a data model that is exposed to clients through MVC and Web API controllers, as I demonstrated in Chapter 2.

Understanding Native Applications

The rise of smartphones and tablets means that many applications are delivered as native clients, rather than as HTML content in a browser window. Native applications still require data and perform operations on that data, which is readily supported through Web API. Web API delivers the data, and the native applications are responsible for processing the data they receive and displaying it to the user. Figure 4-4 shows a mix of client types being supported by an ASP.NET application.

9781484200865_Fig04-04.jpg

Figure 4-4. Supporting mixed client types in an ASP.NET application

Smartphones and tablets are not the only kinds of native application, and just about any application that can send HTTP requests and process common data formats can consume a web service. Smartphones are the most numerous native clients, especially if you are creating an Internet-facing application, but you can also use Web API to support desktop clients, embedded devices, and smart TVs. I don’t describe native applications in this book, but the web services that I create with Web API can easily be consumed by any kind of client.

Understanding Shared-Model Applications

The implicit assumption in Figure 4-4 is that the model state is stored persistently and that all of the applications that need to access the model will do so through the data store, which is typically a database of some kind.

The problem with this approach is that databases are good at managing data but do not have means to consistently enforce features such as authorization or logging in a way that makes sense for the application. For example, it may be possible to log a particular SQL query but not what application function the user was performing that led to the query.

In addition, if there are multiple applications that need to share the model data, it can be difficult to manage schema and data changes without upgrading the database and all of the applications in lock-step, which requires careful planning and testing.

An alternative approach is to use a web service to mediate access to the data store from multiple applications, providing an abstraction from the storage implementation and isolating the applications from changes in the way that data is stored. Figure 4-5 shows this approach.

9781484200865_Fig04-05.jpg

Figure 4-5. Using a web service to mediate access to the data store

Using a web service as a model mediator can impact performance, but the benefits can be worth it if the way that the data is stored is particularly difficult to work with or is expected to change often. In this configuration, the MVC applications are treated just like any other client of the web service and combine the data and markup in the view.

Understanding Service Applications

Service applications don’t interact directly with users. Instead, they obtain data from a web service and package or process it for a different kind of client. Service applications add some kind of value to the web service, perhaps by combining data and operations from multiple web services into a single API or performing complex calculations. Supporting service clients can be a good way to make data available to a wider audience, which may go far beyond your existing user base.

From the perspective of the application, there is little difference between supporting a native application and a service application; the same HTTP requests are received and processed to retrieve data or update the model, and the way in which the data is processed or presented is not known to the web service.

Image Tip  You can see a good example at http://fitbit.com of an API that reaches a wider audience through service applications. Fitbit sells hardware devices that monitor activity levels and provides a web application that collects the activity data. The data and user information are exposed through an API, which has allowed a substantial ecosystem of other sites and services to thrive. Supporting service clients can be useful if your business model isn’t tied to drawing users into your application.

Understanding Simple Web Services

The HTTP web service that I created with ASP.NET Web API in Chapter 2 is what is referred to as a simple web service or, to use a term that has more resonance for an MVC framework developer, a tightly coupled web service. The tight coupling refers to the fact that the client has to have prior knowledge of how the web service has been designed in order to consume the web service.

As an example of prior knowledge, the client in the PartyInvites application needs to know that new RSVP responses are submitted as POST requests to the /api/rsvp/add URL. There is nothing in the responses sent by the Web API controller to indicate that the /api/rsvp/add URL exists; the client needs to have this information ahead of time. This isn’t hard to arrange when the client code is delivered from an MVC framework application because you can embed the information in a view, it becomes more of a problem for native clients of a web service, such as iPhone and Android applications.

As you may expect if you are familiar with the MVC pattern, the problem with tight coupling is that it makes it hard to maintain the application because changes have to be made to the Web API controller and the client JavaScript code at the same time.

It also makes it difficult to use the Web API controller beyond its original purpose because controllers for simple web services tend to offer only action methods for the specific functions that the original client requires. As an example, if I needed to add an administration-type client that allows me to list, edit, and delete guest responses, then I need to extend the functionality of the controller—something that prevents clients from being created without the coordination of the web service developer.

The alternative is to create a web service that doesn’t require the client to have any prior knowledge of the web service, which is the essence of what REST is all about. I describe REST and RESTful web services in the next section and demonstrate how ASP.NET Web API can be used to create them, but before I move on, I want to emphasize that you should not dismiss simple web services out of hand.

Despite the problems that arise from tight coupling, simple web services can transform a round-trip MVC framework application to the single-page model with just a few lines of C# and JavaScript code. You saw this in Chapter 2, where I created the following Web API controller.

using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using PartyInvites.Models;

namespace PartyInvites.Controllers {

    public class RsvpController : ApiController {

        [HttpGet]
        public IEnumerable<GuestResponse> Attendees() {
            return Repository.Responses.Where(x => x.WillAttend == true);
        }

        [HttpPost]
        public void Add(GuestResponse response) {
            if (ModelState.IsValid) {
                Repository.Add(response);
            }
        }
    }
}

It is hard to beat the level of return for such little investment of effort. Simple web services are perfectly acceptable for situations where you are confident that the only client will be delivered by the MVC framework and you know that the rate of change will be low and not driven by third parties (in other words, you are not trying to create an API that can be consumed by a wider audience outside the scope of the MVC framework application). Table 4-1 summarizes simple web services and the situations in which they can be usefully applied.

Table 4-1. Putting Simple Web Services in Context

Question

Answer

What is it?

Simple web services support just the features required for the client of a single application.

When should I use it?

Simple web services are quick to set up and are useful when you don’t expect to add additional types of clients or need to significantly enhance the functionality that the existing client delivers.

What do I need to know?

Simple web services are tightly coupled to their clients, which makes it more difficult to add additional types of client or to change existing clients without also modifying the web service and the supporting MVC framework application.

Understanding RESTful Web Services

The most commonly used pattern to create loosely coupled web services is Representational State Transfer (REST). REST is a general-purpose pattern that, when applied to a web service, creates what is known as a RESTful web service.

THE DANGER OF DESIGN PATTERNS

Like just about all useful design patterns, REST is the subject of endless arguments about what is really RESTful and what is not. These arguments are a waste of time, and you should ignore them. Patterns are templates that you can customize for your own needs. The goal behind RESTful web services is to ensure that the client and ASP.NET Web API controller are loosely coupled, and only you know which aspects of the REST pattern will help you achieve that goal. I stopped arguing with pattern zealots when I realized that the least-skilled programmers are the ones who shout the loudest. My advice is to focus on delivering good software and borrow from and adapt patterns any way you need to get the job done.

In the sections that follow, I describe how to design a RESTful web service API using the GuestResponse model class that I defined for the PartyInvites application in Chapter 2. As a reminder, Listing 4-1 shows the definition of the model class.

Listing 4-1. The Definition of the GuestResponse Model Class

using System.ComponentModel.DataAnnotations;

namespace PartyInvites.Models {
    public class GuestResponse {
        [Required]
        public string Name { get; set; }
        [Required]
        public string Email { get; set; }
        [Required]
        public bool? WillAttend { get; set; }
    }
}

As a reminder, here is how instances of the GuestResponse class are rendered as JSON:

[{"Name":"Bob","Email":"[email protected]","WillAttend":true},
 {"Name":"Alice","Email":"[email protected]","WillAttend":true},
 {"Name":"Paul","Email":"[email protected]","WillAttend":true}]

I am not going to implement the web service in this chapter; I will just define the API that I need. You can see how I implement a RESTful web service in Chapter 6, where I build the more realistic SportsStore application to show the end-to-end implementation process for working with ASP.NET Web API.

Image Tip  Although I use JSON throughout this chapter, ASP.NET Web API is capable of generating different data formats to suit different client requirements. See Chapters 1113 for details.

The reason I don’t write the code in this chapter is that I want to focus on the design of an effective and useful web service API free of the details of its implementation. Understanding why RESTful web services are useful is important, not least because they are more complex and complicated to design. As you will learn, decoupling the client from a web service requires more of an investment of time and effort, which is then paid back through increased flexibility and maintainability, much like the initial investment required to lay the foundation for an MVC framework application. Table 4-2 summarizes RESTful web services and the situations in which they can be usefully applied.

Table 4-2. Putting RESTful Web Services in Context

Question

Answer

What are they?

RESTful web services are useful for decoupling clients and the web services they consume. They require more design and development effort, but they make it easier to maintain the web service.

When should I use them?

You should use RESTful web services when clients are being developed by third parties or when you expect a high rate of change in the API delivered by the web service.

What do I need to know?

You have several choices about how RESTful you make your web service and, as a consequence, how loosely coupled the client and web service are. The less prior knowledge a client requires to consume a web service, the more RESTful that service is.

Embracing HTTP

The core foundation of RESTful web services is to define operations on the model using a combination of HTTP verbs and unique URLs to refer to individual data objects and collections of those objects.

Image Tip  The terms verbs and methods are equivalent when referring to HTTP and can be used interchangeably. I tend to refer to methods when I am writing MVC framework views (because the form element defines a method attribute) and verbs when writing web services.

Here is an example of a URL that uniquely represents the RSVP response from a user called Bob (I have left out the part of the URL that specifies the protocol, hostname, and port because all of these are going to be constant for my example):

/api/rsvp/bob

In a RESTful web service, I use this URL whenever I want to perform an operation on the GuestResponse object that describes Bob’s attendance at the party. To tell the web service what kind of operation I want to perform, I make an HTTP request that targets the URL and specify one of the HTTP verbs in the request.

Image Note  REST is a general-purpose pattern that has found a home in the world of web services, but since this is a book about web services, I am going to treat REST and RESTful web services as being the same thing so that I don’t get tied up in making fine-grained distinctions that don’t have any real impact on Web API development.

You are already familiar with at least two of the HTTP verbs from their use in the MVC framework: GET and POST. What you might not know is that the HTTP specification contains additional verbs and that, in a RESTful web service, these are used to indicate what kind of operation is being requested on the data object identities by the URL in the request. Table 4-3 shows how combining an HTTP verb with a URL can be used to request that a web service perform an operation. Some operations require the client to send data to the server or the server to send data to the client, and I have included this information in the table.

Table 4-3. Combining HTTP Verbs with URLs to Specify a Web Service API

Table4-3.jpg

Image Tip  A web service doesn’t have to support all the verbs listed in Table 4-3. It wouldn’t make sense for a read-only web service to support the DELETE, POST, and PUT verbs, for example, and you need to implement support only for the verbs you require.

The contents of the table define the web service API, and a client-side developer can use this information to consume the web service. This kind of API embraces HTTP by combining URLs and HTTP verbs, which feels exciting and dynamic and like a definite improvement over URLs derived from arbitrary methods names in a controller class, but the truth is somewhat different because the client and server are still too tightly coupled for comfort. I explain why in the following sections.

USING SAFE AND IDEMPOTENT HTTP VERBS

There is no standard mapping of HTTP verbs to web service operations, although the one I describe in Table 4-3 is common. You can use any HTTP verb that you like for your web services, as long as you understand the importance of the safe and idempotent HTTP verbs.

Safe verbs have no side effects. The most commonly used safe verb is GET, and when you receive a GET request, you may not perform any action that alters the state of the data model. All you may do is return the data that has been requested and, optionally, perform cross-cutting activities such as logging and caching.

Idempotent verbs, such as PUT and DELETE, are allowed to modify the data model, but multiple requests with the same verb to the same URL should have the same effect as a single request. The practical effect of this is that you should use URLs to uniquely identify resources, rather than relying on the relationship between data items. For example, if you support a URL such as /api/rsvp/first that refers to the first data object in the repository, accepting a DELETE request for that URL should not cause the data items to shuffle so that there is a new “first” object. You must also write your web service so that it doesn’t generate an error when receiving multiple requests such as a DELETE request for a data object that has already been removed from the repository.

Be careful with the POST verb; it is not necessarily safe or idempotent, and you have some flexibility about how you respond to multiple requests that target the same URL. Most web services will treat a POST request as a PUT request if there is already a matching data item, but you can choose to create a new object or report an error depending on the needs of your data model.

Adding Data Discovery

Uniquely identifying each data object—more properly known as a resource in REST—with a URL is an excellent idea, but it presents a problem: how does the client discover the set of data objects and the URLs that refer to them?

The solution is to create a collection URL, which returns all of the data objects in the model. The convention is that the collection of data objects is retrieved using the root URL that identifies individual objects. In my API, this means that the URL /api/rsvp would return all of the data objects in the model. Table 4-4 shows the addition of the collection URL to the web service API.

Table 4-4. Adding a Collections URL to the Web Service API

Table4-4.jpg

Filtering the Collection

Most clients don’t need to retrieve all of the data in the model, so the convention is to allow clients to narrow the data returned by the collection URL by using query string parameters. For example, to obtain the set of attendees, for which I added a specific action method in Chapter 2, the client would send a GET request to the following URL:

/api/rsvp?WillAttend=true

The web service can ignore the query string and return all of the data objects in the model, but it is generally a good idea to support this convention so that you are not transferring endless amounts of data that clients don’t require and that will be discarded. Table 4-5 shows my revised API.

Table 4-5. Adding Collections Filtering to the Web Service API

Table4-5.jpg

Image Tip  A common variation on this pattern is to build the filter into the URL, rather than relying on the query string. For example, in Chapter 6, I define a web service that returns all of its model objects when the URL /api/products is requested. The object that has the unique identifier 100, for example, would be accessed via the URL /api/products/100. Web API makes it easy to support both URL formats.

This is an example of what I mean about pragmatism in design patterns—even as I am trying to minimize the amount of prior knowledge that the client requires, I am extending my web service API using a convention that both the client and the server need to understand. There is a balance to be found between client-server coupling and applying sensible optimizations, which differs for each project. There is no universal right approach, and you should use your judgment to decide when following a design pattern doesn’t make sense.

Summary

In this chapter, I described the ways in which ASP.NET Web API can be used to deliver web services to a set of different and disparate clients. I also described the two main categories of web services: simple web services, like the one I created in Chapter 2, and RESTful web services, which I demonstrate in the next chapter. I explained that RESTful web services require more design and development effort but produce loosely coupled software systems that are easier to manage and maintain.

This chapter has been a little abstract in nature because I wanted to separate the design of a RESTful web service API from the implementation detail. But don’t worry if you have found it hard going because in the next chapter I revert to showing you code examples to implement the concepts that I have described in this chapter.

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

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