Chapter 4. Defining the application’s HTTP interface

This chapter covers

  • Defining the URLs that the web application responds to
  • Mapping HTTP requests to Scala methods for defined URLs
  • Mapping HTTP request data to type-safe Scala objects
  • Validating HTTP form data
  • Returning a response to the HTTP client

This chapter is all about controllers, at least from an architectural perspective. From a more practical point of view, this chapter is about your application’s URLs and the data that the application receives and sends over HTTP.

In this chapter, we’re going to talk about designing and building a web-based product catalog for various kinds of paperclips that allows you to view and edit information about the many different kinds of paperclips you might find in a paperclip manufacturer’s warehouse.

4.1. Designing your application’s URL scheme

If you were to ask yourself how you designed the URL scheme for the last web application you built, your answer would probably be that you didn’t. Normally, you build a web application, and its pages turn out to have certain URLs; the application works, and you don’t think about it. This is an entirely reasonable approach, particularly when you consider that many web frameworks don’t give you much choice in the matter.

Rails and Django, on the other hand, have excellent URL configuration support. If that’s what you’re using, then the Java EE examples in the next few sections will probably make your eyes hurt, and it would be less painful to skip straight to section 4.1.4.

4.1.1. Implementation-specific URLs

If you ever built a web application with Struts 1.x, you’ve seen a good example of framework-specific implementation details in your URLs. Struts has since been improved upon, and although it’s now obsolete, it was once the most popular Java web framework.

Struts 1.x has an action-based MVC architecture that isn’t all that different from Play’s. This means that to display a product details page, which shows information about a specific product, we’d write a ProductDetailsAction Java class, and access it with a URL such as this:

/product.do

In this URL, the .do extension indicates that the framework should map the request to an action class, and product identifies which action class to use.

We’d also need to identify a specific product, such as by specifying a unique numeric EAN code in a query string parameter:

/product.do?ean=5010255079763
EAN Identifiers

The EAN identifier is an international article number, introduced in chapter 2.

Next, we might extend the action class to include additional Java methods, for variations such as an editable version of the product details, with a different URL:

/product.do?ean=5010255079763&method=edit

When we built web applications like this, they worked, and all was good. More or less. But what many web application developers took for granted, and still do, is that this URL is implementation-specific.

First, the .do doesn’t mean anything and is just there to make the HTTP-to-Java interface work; a different web framework would do something different. You could change the .do to something else in the Struts configuration, but to what? After all, a “file extension” means something, but it doesn’t mean anything for a URL to have an extension.

Second, the method=edit query string parameter was a result of using a particular Struts feature. Refactoring your application might mean changing the URL to something like this:

/productEdit.do?ean=5010255079763

If you don’t think changing the URL matters, then this is probably a good time to read Cool URIs Don’t Change, which Tim Berners-Lee wrote in 1998 (http://www.w3.org/Provider/Style/URI.html), adding to his 1992 WWW style guide, which is an important part of the documentation for the web itself.

Cool URIs Don’t Change

A fundamental characteristic of the web is that hyperlinks are unidirectional, not bidirectional. This is both a strength and a weakness: it lowers the barrier to linking by not requiring you to modify the target resource, at the cost of the risk that the link will “break” because the target resource stops being available at that URL.

You should care about this because not only do published resources have more value if they’re available for longer, but also because people expect them to be available in the future. Besides, complaints about broken links get annoying.

The best way to deal with this is to avoid breaking URLs in the first place, both by using server features that allow old URLs to continue working when new URLs are introduced, and to design URLs so that they’re less likely to change.

4.1.2. Stable URLs

Once you understand the need for stable URLs, you can’t avoid the fact that you have to give them some forethought. You have to design them. Designing stable URLs may seem like a new idea to you, but it’s a kind of API design, not much different from designing a public method signature in object-oriented API design. Tim Berners-Lee tells us how to start: “Designing mostly means leaving information out.”

Designing product detail web page URLs that are more stable than the Struts URLs we saw earlier means simplifying them as much as possible by avoiding any implementation-specific details. To do this, you have to imagine that your web application framework doesn’t impose any constraints on your URLs’ contents or structure.

If you didn’t have any constraints on what your URLs looked like, and you worked on coming up with the simplest and clearest scheme possible, you might come up with the following URLs:

These URLs are stable because they’re “clean”—they have no unnecessary information or structure. We’ve solved the problem of implementation-specific URLs. But that’s not all: you can use URL design as the starting point for your whole application’s design.

4.1.3. Java Servlet API—limited URL configuration

Earlier in this chapter we explained that web applications built with Struts 1.x usually have URLs that contain implementation-specific details. This is partly due to the way that the Java Servlet API maps incoming HTTP requests to Java code. Servlet API URL mapping is too limited to handle even our first three example URLs, because it only lets you match URLs exactly, by prefix or by file extension. What’s missing is a notion of path parameters that match variable segments of the URL, using URL templates:

/product/{ean}/edit

In this example, {ean} is a URL template for a path parameter called ean. URL parsing is about text processing, which means we want a flexible and powerful way to specify that the second segment contains only digits. We want regular expressions:

/product/(d+)/edit

None of the updates to the Servlet specification have added support for things like regular expression matching or path parameters in URLs. The result is that the Servlet API’s approach isn’t rich enough to enable URL-centric design.

Sooner or later, you’ll give up on URL mapping, using the default mapping for all requests, and writing your own framework to parse URLs. This is what Servlet-based web frameworks generally do these days: map all requests to a single controller Servlet, and add their own useful URL mapping functionality. Problem solved, but at the cost of adding another layer to the architecture. This is unfortunate, because a lot of web application development over the last 10 years has used web frameworks based on the Java Servlet API.

What this all means is that instead of supporting URL-centric design, the Servlet API provides a minimal interface that’s almost always used as the basis for a web framework. It’s as if Servlet technology was a one-off innovation to improve on the 1990s’ Common Gateway Interface (CGI), with no subsequent improvements to the way we build web applications.

4.1.4. Benefits of good URL design

To summarize this section on designing your application’s URL scheme, here are several benefits of good URL design:

  • A consistent public APIThe URL scheme makes your application easier to understand by providing an alternative machine-readable interface.
  • The URLs don’t changeAvoiding implementation-specifics makes the URLs stable, so they don’t change when the technology does.
  • Short URLsShort URLs are more usable; they’re easier to type or paste into other media, such as email or instant messages.

4.2. Controllers—the interface between HTTP and Scala

Controllers are the application components that handle HTTP requests for application resources identified by URLs. This makes your application’s URLs a good place to begin our explanation of Play framework controllers.

In Play, you use controller classes to make your application respond to HTTP requests for URLs, such as the product catalog URLs:

/products
/product/5010255079763
/product/5010255079763/edit

With Play, you map each of these URLs to the corresponding method in the controller class, which defines three action methods—one for each URL.

4.2.1. Controller classes and action methods

We’ll start by defining a Products controller class, which will contain four action methods for handling different kinds of requests: list, details, edit, and update (see figure 4.1). The list action, for example, will handle a request for the /products URL and will generate a product list result page. Similarly, details shows product details, edit shows an editable product details form, and update modifies the server-side resource.

Figure 4.1. A controller handles an HTTP request by invoking an action method that returns a result.

In the next section, we’ll explain how Play selects the list action to process the request, instead of one of the other three actions. We’ll also return to the product list result later in the chapter, when we look at how a controller generates an HTTP response. For now, we’ll focus on the controller action.

A controller is a Scala object that’s a subclass of play.api.mvc.Controller, which provides various helpers for generating actions. Although a small application may only have a single controller, you’ll typically group related actions in separate controllers.

An action is a controller method that returns an instance of play.api.mvc.Action. You can define an action like this:

This constructs a Request => Result Scala function that handles the request and returns a result. NotImplemented is a predefined result that generates the HTTP 501 status code to indicate that this HTTP resource isn’t implemented yet, which is appropriate, because we won’t look at implementing the body of action methods, including using things like NotImplemented, until later in this chapter.

The action method may also have parameters, whose values are parsed from the HTTP request. For example, if you’re generating a paginated list, you can use a pageNumber parameter:

def list(pageNumber: Int) = Action {
  NotImplemented
}

The method body typically uses the request data to read or update the model and to render a view. More generally, in MVC, controllers process events, which can result in updates to the model and are also responsible for rendering views. Listing 4.1 shows an outline of the Scala code for our Products controller.

Listing 4.1. A controller class with four action methods

Each of the four methods corresponds to one of the three product catalog URLs:

As you can see, there isn’t a fourth URL for the update method. This is because we’ll use the second URL to both fetch and update the product details, using the HTTP GET and PUT methods respectively. In HTTP terms, we’ll use different HTTP methods to perform different operations on a single HTTP resource.

Note that web browsers generally only support sending GET and POST requests from hyperlinks and HTML forms. If you want to send PUT and DELETE requests, for example, you’ll have to use a different client, such as custom JavaScript code.

We’ll get back to the interactions with the model and views later in the chapter. For now, let’s focus on the controller. We haven’t yet filled in the body of each action method, which is where we’ll process the request and generate a response to send back to the HTTP client (see figure 4.2).

Figure 4.2. Requests are mapped by HTTP method and URL to actions that generate web pages.

In general, an action corresponds roughly to a page in your web application, so the number of actions will generally be similar to the number of pages. Not every action corresponds to a page, though: in our case, the update action updates a product’s details and then sends a redirect to a details page to display the updated data.

You’ll have relatively few controllers, depending on how you choose to group the actions. In an application like our product list, you might have one controller for pages and the functionality related to products, another controller for the warehouses that products are stored in, and another for users of the application—user-management functionality.

Group controllers by model entity

Create one controller for each of the key entities in your application’s high-level data model. For example, the four key entities—Product, Order, Warehouse, and User—might correspond to a data model with more than a dozen entities. In this case, it’d probably be a good idea to have four controller classes: Products, Orders, Warehouses, and Users. Note that it’s a useful convention to use plural names for controllers to distinguish the Products controller from the Product model class.

In Play, each controller is a Scala object that defines one or more actions. Play uses an object instead of a class because the controller doesn’t have any state; the controller is used to group some actions. This is where you can see Play’s stateless MVC architecture.

Don’t define a var in a controller object

A controller must not have any state, so its fields can only be constant values, defined using the val keyword. If you see a controller field declared as a var, that’s probably a coding error and a source of bugs.

Each action is a Scala function that takes an HTTP request and returns an HTTP result. In Scala terms, this means that each action is a function Request[A] => Result whose type parameter A is the request body type.

This action is a method in the controller class, which is the same as saying that the controller layer processes an incoming HTTP request by invoking a controller class’s action method. This is the relationship between HTTP requests and Scala code in a Play application.

More generally, in an action-based web framework such as Play, the controller layer routes an HTTP request to an action that handles the request. In an object-oriented programming language, the controller layer consists of one or more classes, and the actions are methods in these classes.

The controller layer is therefore the mapping between stateless HTTP requests and responses and the object-oriented model. In MVC terms, controllers process events (HTTP requests in this case), which can result in updates to the model. Controllers are also responsible for rendering views. This is a push-based architecture where the actions “push” data from the model to a view.

4.2.2. HTTP and the controller layer’s Scala API

Play models controllers, actions, requests, and responses as Scala traits in the play.api.mvc package—the Scala API for the controller layer. This MVC API mixes the HTTP concepts, such as the request and the response, with MVC concepts such as controllers and actions.

Only import play.api classes

The Play Scala API package names all start with play.api. Other packages, such as play.mvc, are not the packages you’re looking for.

The following MVC API traits and classes correspond to HTTP concepts and act as wrappers for the corresponding HTTP data:

  • play.api.mvc.CookieAn HTTP cookie: a small amount of data stored on the client and sent with subsequent requests
  • play.api.mvc.RequestAn HTTP request: HTTP method, URL, headers, body, and cookies
  • play.api.mvc.RequestHeaderRequest metadata: a name-value pair
  • play.api.mvc.ResponseAn HTTP response, with headers and a body; wraps a Play Result
  • play.api.mvc.ResponseHeaderResponse metadata: a name-value pair

The controller API also adds its own concepts. Some of these are wrappers for the HTTP types that add structure, such as a Call, and some represent additional controller functionality, such as Flash. Play controllers use the following concepts in addition to HTTP concepts:

  • play.api.mvc.ActionA function that processes a client Request and returns a Result
  • play.api.mvc.CallAn HTTP request: the combination of an HTTP method and a URL
  • play.api.mvc.ContentAn HTTP response body with a particular content type
  • play.api.mvc.ControllerA generator for Action functions
  • play.api.mvc.FlashA short-lived HTTP data scope used to set data for the next request
  • play.api.mvc.ResultThe result of calling an Action to process a Request, used to generate an HTTP response
  • play.api.mvc.SessionA set of string keys and values, stored in an HTTP cookie

Don’t worry about trying to remember all of these concepts. We’ll come across the important ones again, one at a time, in the rest of this chapter.

4.2.3. Action composition

You’ll often want common functionality for several controller actions, which might result in duplicated code. For example, it’s a common requirement for access to be restricted to authenticated users, or to cache the result that an action generates. The simple way to do this is to extract this functionality into methods that you call within your action method, as in the following code:

def list = Action {
  // Check authentication.
  // Check for a cached result.

  // Process request...
  // Update cache.
}

But we can do this a better way in Scala. Actions are functions, which means you can compose them to apply common functionality to multiple actions. For example, you could define actions for caching and authentication and use them like this:

def list =
  Authenticated {
    Cached {
      Action {

        // Process request...
      }
    }
  }

This example uses Action to create an action function that’s passed as a parameter to Cached, which returns a new action function. This, in turn, is passed as a parameter to Authenticated, which decorates the action function again.

Now that we’ve had a good look at actions, let’s look at how we can route HTTP requests to them.

4.3. Routing HTTP requests to controller actions

Once you have controllers that contain actions, you need a way to map different request URLs to different action methods. For example, the previous section described mapping a request for the /products URL to the Products.list controller action, but it didn’t explain how the list action is selected.

At this point, we mustn’t forget to include the HTTP method in this mapping as well, because the different HTTP methods represent different operations on the HTTP resource identified by the URL. After all, the HTTP request GET /products should have a different result than DELETE /products. The URL path refers to the same HTTP resource—the list of products—but the HTTP methods may correspond to different basic operations on that resource. As you may recall from our URL design, we’re going to use the PUT method to update a product’s details.

In Play, mapping the combination of an HTTP method and a URL to an action method is called routing. The Play router is a component that’s responsible for mapping each HTTP request to an action and invoking it. The router also binds request parameters to action method parameters. Let’s add the routing to our picture of how the controller works, as shown in figure 4.3.

Figure 4.3. Selecting the route that’s the mapping from GET /products to Products.list

The router performs the mapping from GET /products to Products.list as a result of selecting the route that specifies this mapping. The router translates the GET /products request to a controller call and invokes our Products.list controller action method. The controller action method can then use our model classes and view templates to generate an HTTP response to send back to the client.

4.3.1. Router configuration

Instead of using the router programmatically, you configure it in the routes file at conf/routes. The routes file is a text file that contains route definitions, also called routes. The great thing about this approach is that your web application’s URLs—its public HTTP interface—are all specified in one place, which makes it easier for you to maintain a consistent URL design. This means you have no excuse for not having nice, clean, well-structured URLs in your application.

For example, to add to our earlier example, our product catalog will use the HTTP methods and URLs listed in table 4.1.

Table 4.1. URLs for the application’s HTTP resources

Method

URL path

Description

GET / Home page
GET /products Product list
GET /products?page=2 The product list’s second page
GET /products?filter=zinc Products that match zinc
GET /product/5010255079763 The product with the given code
GET /product/5010255079763/edit Edit page for the given product
PUT /product/5010255079763 Update the given product details

This URL scheme is the result of our URL design, and it’s what we’ll specify in the router configuration. This table is the design, and the router configuration is the code. In fact, the router configuration won’t look much different than this.

The routes file structure is line-based: each line is either a blank line, a comment line, or a route definition. A route definition has three parts on one line, separated by whitespace. For example, our application’s product list has the route definition shown in figure 4.4.

Figure 4.4. routes file’s route definition syntax

The call definition must be a method that returns an action. We can start with the simplest possible example, which is an HTTP GET request for the / URL path, mapped to the home action method in the Products controller class:

GET /   controllers.Products.home()

Similarly, this is the route for the products list:

GET /products   controllers.Products.list()

If the call definition returns an action method that has parameters, the router will map query-string parameters from the request URL to any action method parameters that have the same names. For example, let’s add an optional page number parameter, with a default value, to the product list:

GET /products   controllers.Products.list(page: Int ?= 1)

The ?= syntax for an optional parameter isn’t normal Scala syntax, and it’s only used in the routes file. You can also use = for fixed parameter values that aren’t specified in the URL (page: Int = 1), and Option for optional parameters that may or may not be included in the query string (page: Option[Int]).

You’d implement the filter parameter the same way as the page parameter—as an additional parameter in the list action method. In the action method, you’d use these parameters to determine which products to list.

The URL pattern may declare URL path parameters. For example, the route definition for a product details URL that includes a unique product identifier, such as /product/5010255079763, is as follows:

GET /product/:ean   controllers.Products.details(ean: Long)
Use external identifiers in URLs

Use unique externally defined identifiers from your domain model in URLs instead of internal identifiers, such as database primary keys, when you can, because it makes your API and data more portable. If the identifier is an international standard, so much the better.

Note that in both cases, the parameter types must match the action method types, or you’ll get an error at compile time. This parameter binding is type-safe, as described in the next section.

Putting this all together, we end up with the following router configuration. In a Play application, this is the contents of the conf/routes file:

GET /                    controllers.Application.home()

GET /products            controllers.Products.list(page: Int ?= 1)

GET /product/:ean        controllers.Products.details(ean: Long)

GET /product/:ean/edit   controllers.Products.edit(ean: Long)

PUT /product/:ean        controllers.Products.update(ean: Long)

This looks similar to our URL design in table 4.1. This isn’t a coincidence: the routing configuration syntax is a direct declaration, in code, of the URL design. We might’ve written the table of URLs as in table 4.2, referring to the controllers and actions, making it even more similar.

Table 4.2. URLs for the application’s HTTP resources

Method

URL path

Mapping

GET / Application controller’s home action
GET /products Products.list action, page parameter
GET /product/5010255079763 Products.details action, ean parameter
GET /product/5010255079763/edit Products.edit action, ean parameter
PUT /product/5010255079763 Products.update action, ean parameter

The only thing missing from the original design is the descriptions, such as “Details for the product with the given EAN code.” If you want to include more information in your routes file, you could include these descriptions as line comments for individual routes, using the # character:

# Details for the product with the given EAN code
GET /product/:ean   controllers.Products.details(ean: Long)

The benefit of this format is that you can see your whole URL design in one place, which makes it more straightforward to manage than if the URLs were specified in many different files.

Note that you can use the same action more than once in the routes file to map different URLs to the same action.[1] But the action method must have the same signature in both cases; you can’t map URLs to two different action methods that have the same name but different parameter lists.

1 This causes a compiler warning about “unreachable code” that you can ignore.

Keep your routes tidy

Keep your routing configuration tidy and neat, avoiding duplication and inconsistencies, because this is the same as refactoring your application’s URL design.

Most of the time, you’ll only need to use the routes file syntax, which we covered in the previous section, but you’ll find some special cases where additional router configuration features are useful.

4.3.2. Matching URL path parameters that contain forward slashes

URL path parameters are normally delimited by slashes, as in the example of our route configuration for URLs like /product/5010255079763/edit, whose 13-digit number is a path parameter.

Suppose we want to extend our URL design to support product photo URLs that start with /photo/, followed by a file path, like this:

/photo/5010255079763.jpg
/photo/customer-submissions/5010255079763/42.jpg
/photo/customer-submissions/5010255079763/43.jpg

You could try using the following route configuration, with a path parameter for the photo filename:

This route doesn’t work because it only matches the first of the three URLs. The :file path parameter syntax doesn’t match Strings that include slashes.

The solution is a different path parameter syntax, with an asterisk instead of a colon, that matches paths that include slashes:

Slashes are a special case of a more general requirement to handle specific characters differently.

4.3.3. Constraining URL path parameters with regular expressions

In your URL design, you may want to support alternative formats for a URL path parameter. For example, suppose that you’d like to be able to address a product using an abbreviated product alias as an alternative to its EAN code:

You could try using the following route configuration to attempt to support both kinds of URLs:

This doesn’t work because a request for /product/paper-clips-large-plain-1000-pack matches the first route, and the binder attempts to bind the alias as a Long. This results in a binding error:

For request GET /product/paper-clips-large-plain-1000-pack
[Can't parse parameter ean as Long: For input string:
"paper-clips-large-plain-1000-pack"]

The solution is to make the first of the two routes only match a 13-digit number, using the regular expression d{13}. The route configuration syntax is as follows:

This works because a request for /product/paper-clips-large-plain-1000-pack doesn’t match the first route, because the paper-clips-large-plain-1000-pack alias doesn’t match the regular expression. Instead, the request matches the second route; the URL path parameter for the alias is bound to a String object and used as the alias argument to the Products.alias action method.

4.4. Binding HTTP data to Scala objects

The previous section described how the router maps incoming HTTP requests to action method invocations. The next thing that the router needs to do is to parse the EAN code request parameter value 5010255079763. HTTP doesn’t define types, so all HTTP data is effectively text data, which means we have to convert the 13-character string into a number.

Some web frameworks consider all HTTP parameters to be strings, and leave any parsing or casting to types to the application developer. For example, Ruby on Rails parses request parameters into a hash of strings, and the Java Servlet API’s ServletRequest.getParameterValues(String) method returns an array of string values for the given parameter name.

When you use a web framework with a stringly typed HTTP API, you have to perform runtime conversion in the application code that handles the request. This results in code like the Java code in listing 4.2, which is all low-level data processing that shouldn’t be part of your application:

Listing 4.2. Servlet API method to handle a request with a numeric parameter
public void doGet(HttpServletRequest request,
   HttpServletResponse response) throws ServletException, IOException {

   try {
      final String ean = request.getParameter("ean");
      final Long eanCode = Long.parseLong(ean);
      // Process request...


   }
   catch (NumberFormatException e) {
      final int status = HttpServletResponse.SC_BAD_REQUEST;
      response.sendError(status, e.getMessage());
   }
}

Play, along with other modern web frameworks such as Spring MVC, improves on treating HTTP request parameters as strings by performing type conversion before it attempts to call your action method. Compare the previous Java Servlet API example with the Play Scala equivalent:

def details(ean: Long) = Action {
  // Process request...
}

Only when type conversion succeeds does Play call this action method, using the correct types for the action method parameters—Long for the ean parameter, in this case.

In order to perform parameter-type conversion before the router invokes the action method, the router first constructs objects with the correct Scala type to use as parameters. This process is called binding in Play, and it’s handled by various type-specific binders that parse untyped text values from HTTP request data (see figure 4.5).

Figure 4.5. Routing requests: binding parameters and invoking controller actions

In figure 4.5 you can see the routing process, including binding. Here’s what happens when Play’s router handles the request PUT /product/5010255079763.

1.  The router matches the request against configured routes and selects the route: PUT /product/:ean controllers.Products.update(ean: Long)

2.  The router binds the ean parameter using one of the type-specific binders—in this case, the Long binder converts 5010255079763 to a Scala Long object

3.  The router invokes the selected route’s Products.update action, passing 5010255079763L as a parameter.

Binding is special because it means that Play is providing type safety for untyped HTTP parameters. This is part of how Play helps make an application maintainable when it has a large number of HTTP resources: debugging a large number of HTTP routes without this compile-time checking takes much longer. This is because routes and their parameters are more tightly mapped to a controller action, which makes it easier to deal with lots of them.

For example, you can map the following two URLs (for two different resources) to two different actions based on the parameter type:

/product/5010255079763
/product/paper-clips-large-plain-1000

What makes this easier is that a similar URL with a missing parameter, such as /product/, would never be mapped to the action method in the first place. This is more convenient than having to deal with a null value for the productId action method parameter.

Binding applies to two kinds of request data: URL path parameters and query string parameters in HTTP POST requests. The controller layer simplifies this by binding both the same way, which means the action method has the same Scala method parameters no matter which parts of the HTTP request their values come from.

For example, our product details’ route has an ean parameter that will be converted to a Long, which means that the URL path must end in a number. If you send an HTTP request for /product/x, the binding will fail because x isn’t a number, and Play will return an HTTP response with the 400 (Bad Request) status code and an error page, as figure 4.6 shows.

Figure 4.6. The error page that Play shows as a result of a binding error

In practice, this is a client programming error: the Play web application won’t use an invalid URL internally because this is prevented by reverse routing, which is described in section 4.5.

You get the same error if binding fails for a query-string parameter, such as a non-numeric page number, as in the URL /products?page=x.

Play defines binders for a number of basic types, such as numbers, Boolean values, and dates. You can also add binding for custom types, such as your application’s domain model types, by adding your own Formatter implementation.

A common case for binding data to Scala objects is when you want to bind the contents of an HTML form to a domain model object. To do this, you need a form object. Form objects, which map HTTP data to your model, are described in detail in chapter 7.

4.5. Generating HTTP calls for actions with reverse routing

In addition to mapping incoming URL requests to controller actions, a Play application can do the opposite: map a particular action method invocation to the corresponding URL. It might not be immediately obvious why you’d want to generate a URL, but it turns out that this helps with a key aspect of URL-centric design. Let’s start with an example.

4.5.1. Hardcoded URLs

In our product catalog application, we need to be able to delete products. Here are the steps for how this should work:

1.  The user interface includes an HTML form that includes a Delete Product button.

2.  When you click the Delete Product button, the browser sends the HTTP request POST /product/5010255079763/delete (or perhaps a DELETE request for the product details URL).

3.  The request is mapped to a Products.delete controller action method.

4.  The action deletes the product.

The interesting part is what happens next, after the product is deleted. Let’s suppose that after deleting the product, we want to show the updated product list. We could render the product list page directly, but this exposes us to the double-submit problem: if the user “reloads” the page in a web browser, this could result in a second call to the delete action, which will fail because the specified product no longer exists.

Redirect-after-POST

The standard solution to the double-submit problem is the redirect-after-POST pattern: after performing an operation that updates the application’s persistent state, the web application sends an HTTP response that consists of an HTTP redirect.

In our example, after deleting a product, we want the web application (specifically the action method) to send a response that redirects to the product list. A redirect is an HTTP response with a status code indicating that the client should send a new HTTP request for a different resource at a given location:

HTTP/1.1 302 Found
Location: http://localhost:9000/products

Play can generate this kind of response for us, so we should be able to implement the action that deletes a product’s details and then redirects to the list page, as follows:

This looks like it will do the job, but it doesn’t smell too nice because we’ve hardcoded the URL in a string. The compiler can’t check the URL, which is a problem in this example because we mistyped the URL as /proudcts instead of /products. The result is that the redirect will fail at runtime.

Hardcoded URL paths

Even if you don’t make typos in your URLs, you may want to change them in the future. Either way, the result is the same: the wrong URL in a string in your application represents a bug that you can only find at runtime. To put it more generally, a URL is part of the application’s external HTTP interface, and using one in a controller action makes the controller dependent on the layer above it—the routing configuration.

This might not seem important when you look at an example like this, but this approach becomes unmaintainable as your application grows and makes it difficult to safely change the application’s URL interface without breaking things. When forced to choose between broken links and ugly URLs that don’t get refactored for simplicity and consistency, web application developers tend to choose the ugly URLs, and then get the broken links anyway. Fortunately, Play anticipates this issue with a feature that solves this problem: reverse routing.

4.5.2. Reverse routing

Reverse routing is a way to programmatically access the routes configuration to generate a URL for a given action method invocation. This means you can do reverse routing by writing Scala code.

For example, we can change the delete action so that we don’t hardcode the product list URL:

This example uses reverse routing by referring to routes.Products.list(): this is a reverse route that generates a call to the controllers.Products.list() action. Passing the result to Redirect generates the same HTTP redirect to http://localhost:9000/products that we saw earlier. More specifically, the reverse route generates a URL in the form of an HTTP call (a play.api.mvc.Call) for a certain action method, including the parameter values, as shown in figure 4.7.

Figure 4.7. Routing requests to actions, compared to reverse routing actions to requests

Reverse routing in practice

Generating internal URLs in a Play application means making the routing and binding described in the previous sections go backwards. Doing things backwards, and reverse routing in particular, gets confusing if you think about it too much, so it’s easiest to remember it by keeping these two points in mind:[2]

2 Unless your mother tongue is Arabic, in which case it might be less obvious to think of right to left as the “reverse” direction.

  • Routing is when URLs are routed to actions—left to right in the routes file
  • Reverse routing is when call definitions are “reversed” into URLs—right to left

Reverse routes have the advantage of being checked at compile time, and they allow you to change the URLs in the routes configuration without having to update strings in Scala code.

You also need reverse routes when your application uses its URLs in links between pages. For example, the product list web page will include links to individual product details pages, which means generating HTML that contains the details page URL:

<a href="/product/5010255079763">5010255079763 details</a>

Listing 6.4 shows you how to use reverse routing in templates, so you don’t have to hardcode URLs there either.

Avoid literal internal URLs

Refer to actions instead of URLs within your application. A worthwhile and realistic goal is for each of your application’s URLs to only occur once in the source code, in the routes file.

Note that the routes file may define more than one route to a single controller action. In this case, the reverse route from this action resolves to the URL that’s defined first in your routes configuration.

Hypermedia as the engine of application state

In general, a web application will frequently generate internal URLs in views that link to other resources in the application. Making this part of how a web application works is the REST principle of “hypermedia as the engine of application state,” whose convoluted name and ugly acronym HATEOAS obscures its simplicity and importance.

Web applications have the opportunity to be more usable than software with other kinds of user interfaces, because a web-based user interface in an application with a REST architecture is more discoverable.

You can find the application’s resources—their data and their behavior—by browsing the user interface. This is the idea that hypermedia—in this case hypertext in the form of HTML—allows you to use links to discover additional resources that you didn’t already know about.

This is a strong contrast to the desktop GUI software user interfaces that predate the web, whose help functionality was entirely separate or, most of the time, nonexistent. Knowing about one command rarely resulted in finding out about another one.

When people first started using the web, the experience was so liberating that they called it surfing. This is why HATEOAS is so important to web applications, and why the Play framework’s affinity with web architecture makes it inevitable that Play includes powerful and flexible reverse routing functionality to make it easy to generate internal URLs.

Play’s generated reverse routing API

You don’t need to understand how reverse routing works to use it, but if you want to see what’s going on, you can look at how Play does it.

Our example uses reverse routing to generate a call to the Products.list() action, resulting in an HTTP redirect. More specifically, it generates the HTTP request GET /products in the form of an HTTP call (a play.api.mvc.Call) for the action method, including the parameter values.

To make this possible, when Play compiles your application, it also generates and compiles a controllers.ReverseProducts reverse controller whose list method returns the call for GET /products. If we exclude the pageNumber parameter for simplicity, this reverse controller and its list method look like this:

Play generates these Scala classes for all of the controllers, each with methods that return the call for the corresponding controller action method.

These reverse controllers are, in turn, made available in a controllers.routes Java class that’s generated by Play:

The result is that you can use this API to perform reverse routing. You’ll recall from chapter 1 that you can access your application’s Scala API from the Scala console, so let’s do that. First, run the play command in your application’s directory to start the Play console:

$ play
[info] Loading project definition from /samples/ch04/reverse/project
[info] Set current project to reverse
[info] (in build file:/samples/ch04/reverse/)
       _            _
 _ __ | | __ _ _  _| |
| '_ | |/ _' | || |_|
|  __/|_|\____|\__ (_)
|_|            |__/

play! 2.1.1, http://www.playframework.org

> Type "help" or "license" for more information.
> Type "exit" or use Ctrl+D to leave this console.

[reverse] $

Now start the Scala console:

[reverse] $ console
[info] Starting scala interpreter...
[info]
Welcome to Scala version 2.10.0.
Type in expressions to have them evaluated.
Type :help for more information.

scala>

Next, perform reverse routing to get a play.api.mvc.Call object:

scala> val call = controllers.routes.Products.list()
call: play.api.mvc.Call = /products

As you’ll recall from the generated Scala source for the reverse controller’s list method, the Call object contains the route’s HTTP method and the URL path:

scala> val (method, url) = (call.method, call.url)
method: String = GET
url: String = /products

4.6. Generating a response

At this point in the chapter, we’ve looked at a lot of detail about handling HTTP requests, but we still haven’t done anything with those requests. This section is about how to generate an HTTP response to send back to a client, such as a web browser, that sends a request.

An HTTP response consists of an HTTP status code, optionally followed by response headers and a response body. Play gives you total control over all three, which lets you craft any kind of HTTP response you like, but it also gives you a convenient API for handling common cases.

4.6.1. Debugging HTTP responses

It’s useful to inspect HTTP responses because you can check the HTTP headers and the unparsed raw content. Let’s look at two good ways to debug HTTP responses—the first is to use cURL (http://curl.haxx.se/) on the command line and a web browser’s debugging functionality.

To use cURL, use the --request option to specify the HTTP method and --include to include HTTP response headers in the output, followed by the URL. For example,

curl --request GET --include http://localhost:9000/products

Alternatively, web browsers such as Safari (see figure 4.8) and Chrome have a Network debug view that shows HTTP requests and the corresponding response headers and content.

Figure 4.8. The Network debug view in Safari, showing response headers at the bottom

For Firefox, you can use plugins that provide the same information.

4.6.2. Response body

Earlier in the chapter we mentioned a products list resource, identified by the /products URL path. When our application handles a request for this resource, it will return a representation of a list of products. The response body will consist of this representation, in some particular format.

In practice, we use different formats for different kinds of resources, depending on the use case. These are the typical formats:

  • Plain textSuch as an error message, or a lightweight web service response
  • HTMLA web page, including a representation of the resource as well as application user-interface elements, such as navigation controls
  • JSONA popular alternative to XML that’s better suited to Ajax applications
  • XMLData accessed via a web service
  • Binary dataTypically nontext media such as a bitmap image or audio

You’re probably using Play to generate web pages, but not necessarily.

Plain text representation

To output plain text from an action method, add a String parameter to one of the predefined result types, such as Ok:

def version = Action {
  Ok("Version 2.0")
}

This example action returns an HTTP response that consists of the string "Version 2.0".

HTML representation

The canonical web application response is a web page. In principle, a web page is also only a string, but in practice you use a templating system. Play templates are covered in chapter 6, but all you need to know for now is that a template is compiled into a Scala function in the views package. This template function returns content whose type is a format like HTML, rather than only a string.

To render a template, you use the same approach as for plain text: the rendered template is a parameter to a result type’s apply method:

def index = Action {
  Ok(views.html.index())
}

In this example, we call the apply method on the views.html.index object that Play generates from an HTML template. This apply method returns the rendered template in the form of a play.api.templates.Html object, which is a kind of play.api .mvc.Content.

This Content trait is what different output formats have in common. To render other formats, such as XML or JSON, you pass a Content instance in the same way.

JSON representation

Typically, you can output JSON in one of two ways, depending on what you need to do. You either create a JSON template, which works the same way as a conventional HTML template, or you use a helper method to generate the JSON by serializing Scala objects.

For example, suppose you want to implement a web service API that requires a JSON { "status": "success" } response. The easiest way to do this is to serialize a Scala Map, as follows:

In this example, you serialize a Scala object and pass the resulting play.api.libs .json.JsValue instance to the result type. As you’ll see later, this also sets the HTTP response’s Content-Type header.

You can use this approach as the basis of a JSON web service that serves JSON data. For example, if you implement a single-page web application that uses JavaScript to implement the whole user interface, you need a web service to provide model data in JSON format. In this architecture, the controller layer is a data access layer, instead of being part of the HTML user interface layer.

XML representation

For XML output, you have the same options as for JSON output: serialize Scala objects to XML (also called marshaling), or use an XML template.

In Scala, another option is to use a literal scala.xml.NodeSeq. For example, you can pass an XML literal to a result type, as you did when passing a string for plain-text output:

def xml = Action {
  Ok(<status>success</status>)
}
Binary data

Most of the binary data that you serve from a web application will be static files, such as images. We’ll look at how to serve static files later in this chapter.

But some applications also serve dynamic binary data, such as PDF or spreadsheet representations of data, or generated images. In Play, returning a binary result to the web browser is the same as serving other formats: as with XML and JSON, pass the binary data to a result type. The only difference is that you have to manually set an appropriate content type.

For example, suppose our products list application needs the ability to generate bar codes for product numbers in order to print labels that can be later scanned with a bar code scanner, as shown in figure 4.9. We can do this by implementing an action that generates a bitmap image for an EAN 13 bar code.

Figure 4.9. Generated PNG bar code, served as an image/png response

To do this, we’ll use the open-source barcode4j library (http://sourceforge.net/projects/barcode4j/).

First, we’ll add barcode4j to our project’s external dependencies to make the library available. In project/Build.scala, add an entry to the appDependencies list:

val appDependencies = Seq(
  "net.sf.barcode4j" % "barcode4j" % "2.0"
)

Next, we’ll add a helper function that generates an EAN 13 bar code for the given EAN code and returns the result as a byte array containing the PNG image shown in figure 4.9:

def ean13Barcode(ean: Long, mimeType: String): Array[Byte] = {
   import java.io.ByteArrayOutputStream
   import java.awt.image.BufferedImage
   import org.krysalis.barcode4j.output.bitmap.BitmapCanvasProvider
   import org.krysalis.barcode4j.impl.upcean.EAN13Bean

   val BarcodeResolution = 72
   val output: ByteArrayOutputStream = new ByteArrayOutputStream
   val canvas: BitmapCanvasProvider =
     new BitmapCanvasProvider(output, mimeType, BarcodeResolution,
       BufferedImage.TYPE_BYTE_BINARY, false, 0)
   val barcode = new EAN13Bean()
   barcode.generateBarcode(canvas, String valueOf ean)
   canvas.finish
   output.toByteArray
 }

Next, we’ll add a route for the controller action that will generate the bar code:

GET /barcode/:ean controllers.Products.barcode(ean: Long)

Finally, we’ll add a controller action that uses the ean13BarCode helper function to generate the bar code and return the response to the web browser, as shown in listing 4.3.

Listing 4.3. Bar code controller action—app/controllers/Products.scala

As you can see, once you have binary data, all you have to do is pass it to a result type and set the appropriate Content-Type header. In this example, we’re passing a byte array to an Ok result type.

Finally, request http://localhost:9000/barcode/5010255079763 in a web browser to view the generated bar code—see figure 4.9.

Use an HTTP redirect to serve locale-specific static files

One use case for serving binary data from a Play controller is to serve one of several static files based on some application logic. For example, after localizing your application, you may have language-specific versions of graphics files. You could use a controller action to serve the contents of the file that corresponds to the current language, but a simpler solution is to send an HTTP redirect that instructs the browser to request a language-specific URL instead.

4.6.3. HTTP status codes

The simplest possible response that you might want to generate consists of only an HTTP status line that describes the result of processing the request. A response would usually only consist of a status code in the case of some kind of error, such as the following status line:

HTTP/1.1 501 Not Implemented

We’ll get to generating a proper response, such as a web page, later in this chapter. First, let’s look at how you can choose the status code using Play.

We saw this Not Implemented error earlier in this chapter, with action method examples like the following, in which the error was that we hadn’t implemented anything else yet:

To understand how this works, first recall that an action is a function (Request => Result). In this case, the function returns the single NotImplemented value, which is defined as a play.api.mvc.Status with HTTP status code 501. Status is a subclass of the play.api.mvc.Result object, which means that the previous example is the same as this:

def list = Action {
  new Status(501)
}

When Play invokes this action, it calls the function created by the Action wrapper and uses the Result return value to generate an HTTP response. In this case, the only data in the Result object is the status code, which means the HTTP response is a status line:

HTTP/1.1 501 Not Implemented

NotImplemented is one of many HTTP status codes that are defined in the play.api.mvc.Controller class via the play.api.mvc.Results trait. You’d normally use these errors to handle exception cases in actions that normally return a success code and a more complete response. We’ll see examples of this later in this chapter.

Perhaps the only scenario when a successful request wouldn’t generate a response body is when you create or update a server-side resource, as a result of submitting an HTML form or sending data in a web service request. In this case, you don’t have a response body because the purpose of the request was to send data, not to fetch data. But the response to this kind of request would normally include response headers, so let’s move on.

4.6.4. Response headers

In addition to a status, a response may also include response headers: metadata that instructs HTTP clients how to handle the response. For example, the earlier HTTP 501 response example would normally include a Content-Length header to indicate the lack of a response body:

HTTP/1.1 501 Not Implemented
Content-Length: 0

A successful request that doesn’t include a response body can use a Location header to instruct the client to send a new HTTP request for a different resource. For example, earlier in the chapter we saw how to use Redirect in an action method to generate what’s colloquially called an HTTP redirect response:

HTTP/1.1 302 Found
Location: http://localhost:9000/products

Internally, Play implements the Redirect method by adding a Location header for the given url to a Status result:

Status(FOUND).withHeaders(LOCATION -> url)

You can use the same approach if you want to customize the HTTP response. For example, suppose you’re implementing a web service that allows you to add a product by sending a POST request to /products. You may prefer to indicate that this was successful with a 201 Created response that provides the new product’s URL:

HTTP/1.1 201 Created
Location: /product/5010255079763
Content-Length: 0

Given a newly created models.Product instance, as in our earlier examples, you can generate this response with the following code in your action method (this and the next few code snippets are what go inside Action { ... }):

Although you can set any header like this, Play provides a more convenient API for common use cases. Note that, as in section 4.5, we’re using the routes.Products.details reverse route that Play generates from our controllers.Products.details action.

Setting the content type

Every HTTP response that has a response body also has a Content-Type header, whose value is the MIME type that describes the response body format. Play automatically sets the content type for supported types, such as text/html when rendering an HTML template, or text/plain when you output a string response.

Suppose you want to implement a web service API that requires a JSON { "status": "success" } response. You can add the content type header to a string response to override the text/plain default:

val json = """{ "status": "success" }"""
Ok(json).withHeaders(CONTENT_TYPE -> "application/json")

This is a fairly common use case, which is why Play provides a convenience method that does the same thing:

Ok("""{ "status": "success" }""").as("application/json")

As long as we’re simplifying, we can also replace the content type string with a constant: JSON is defined in the play.api.http.ContentTypes trait, which Controller extends.

Ok("""{ "status": "success" }""").as(JSON)

Play sets the content type automatically for some more types: Play selects text/xml for scala.xml.NodeSeq values, and application/json for play.api.libs.json.JsValue values. For example, you saw earlier how to output JSON by serializing a Scala object. This also sets the content type, which means that you can also write the previous two examples like this:

Ok(Json.toJson(Map("status" -> "success")))

The trade-off with this kind of more convenient syntax is that your code is less close to the underlying HTTP API, which means that although the intention is clear, it may be less obvious what’s going on.

Session data

Sometimes you want your web application to “remember” things about what a user’s doing. For example, you might want to display a link to the user’s previous search on every page to allow the user to repeat the previous search request. This data doesn’t belong in the URL, because it doesn’t have anything to do with whatever the current page is. You probably also want to avoid the complexity of adding this data to the application model and storing it in a database on the server (although sooner or later, the marketing department is going to find out that this is possible).

One simple solution is to use session data, which is a map for string key-value pairs (a Map[String,String]) that’s available when processing requests for the current user. The data remains available until the end of the user session, when the user closes the web browser. Here’s how you do it in a controller. First, save a search query in the session:

Ok(results).withSession(
  request.session + ("search.previous" -> query)
)

Then, elsewhere in the application, retrieve the value stored in the session:

val search = request.session.get("search.previous")

To implement Clear Previous Search in your application, you can remove a value from the session with the following:

Ok(results).withSession(
  request.session - "search.previous"
)

The session is implemented as an HTTP session cookie, which means that its total size is limited to a few kilobytes. This means that it’s well-suited to small amounts of string data, such as this saved search query, but not for larger or more complex structures. We’ll address cookies in general later in this chapter.

Don’t cache data in the session cookie

Don’t try to use session data as a cache to improve performance by avoiding fetching data from server-side persistent storage. Apart from the fact that session data is limited to the 4 KB of data that fits in a cookie, this will increase the size of subsequent HTTP requests, which will include the cookie data, and may make performance worse overall.

The canonical use case for session cookies is to identify the currently authenticated user. In fact, it’s reasonable to argue that if you can identify the current user using a session cookie, then that should be the only thing you use cookies for, because you can load user-specific data from a persistent data model instead.

The session Play cookie is signed using the application secret key as a salt to prevent tampering. This is important if you’re using the session data for things like preventing a malicious user from constructing a fake session cookie that would allow them to impersonate another user. You can see this by inspecting the cookie called PLAY_SESSION that’s stored in your browser for a Play application, or by inspecting the Set-Cookie header in the HTTP response.

Flash data

One common use for a session scope in a web application is to display success messages. Earlier we saw an example of using the redirect-after-POST pattern to delete a product from our product catalog application, and then to redirect to the updated products list (in the redirect-after-POST portion of section 4.5.1). When you display updated data after making a change, it’s useful to show the user a message that confirms that the operation was successful—“Product deleted!” in this case.

The usual way to display a message on the products list page would be for the controller action to pass it directly to the products list template when rendering the page. That doesn’t work in this case because of the redirect: the message is lost during the redirect because template parameters aren’t preserved between requests. The solution is to use session data, as described previously.

Displaying a message when handling the next request, after a redirect, is such a common use case that Play provides a special session scope called flash scope. Flash scope works the same way as the session, except that any data that you store is only available when processing the next HTTP request, after which it’s automatically deleted. This means that when you store the “product deleted” message in flash scope, it’ll only be displayed once.

To use flash scope, add values to a response type. For example, to add the “product deleted” message, use this command:

Redirect(routes.Products.flash()).flashing(
  "info" -> "Product deleted!"
)

To display the message on the next page, retrieve the value from the request:

val message = request.flash("info")

You’ll learn how to do this in a page template, instead of in a controller action, in chapter 6.

Setting cookies

The session and flash scopes we previously described are implemented using HTTP cookies, which you can use directly if the session or flash scopes don’t solve your problem.

Cookies store small amounts of data in an HTTP client, such as a web browser on a specific computer. This is useful for making data “sticky” when there’s no user-specific, server-side persistent storage, such as for user preferences. This is the case for applications that don’t identify users.

Avoid using cookies

Most of the time you can find a better way to solve a problem without using cookies directly. Before you turn to cookies, consider whether you can store the data using features that provide additional functionality, such as the Play session or flash scopes, server-side cache, or persistent storage.

Setting cookie values is another special case of an HTTP response header, but this can be complex to use directly. If you do need to use cookies, you can use the Play API to create cookies and add them to the response, and to read them from the request.

Note that one common use case for persistent cookies—application language selection—is built into Play.

4.6.5. Serving static content

Not everything in a web application is dynamic content: a typical web application also includes static files, such as images, JavaScript files, and CSS stylesheets. Play serves these static files over HTTP the same way it serves dynamic responses: by routing an HTTP request to a controller action.

Using the default configuration

Most of the time you’ll want to add a few static files to your application, in which case the default configuration is fine. Put files and folders inside your application’s public/ folder and access them using the URL path /assets, followed by the path relative to public.

For example, a new Play application includes a favorites icon at public/images/favicon.png, which you can access at http://localhost:9000/assets/images/favicon.png. The same applies to the default JavaScript and CSS files in public/javascripts/ and public/stylesheets/. This means that you can refer to the icon from an HTML template like this:

<link href="/assets/images/favicon.png"
      rel="shortcut icon" type="image/png">

To see how this works, look at the default conf/routes file. The default HTTP routing configuration contains a route for static files, called assets:

GET /assets/*file   controllers.Assets.at(path="/public", file)

This specifies that HTTP GET requests for URLs that start with /assets/ are handled by the Assets controller’s at action, which takes two parameters that tell the action where to find the requested file.

In this example, the path parameter takes a fixed value of "/public". You can use a different value for this parameter if you want to store static files in another folder, such as by declaring two routes:

GET /images/*file   controllers.Assets.at(path="/public/images", file)
GET /styles/*file   controllers.Assets.at(path="/public/styles", file)

The file parameter value comes from a URL path parameter. You may recall from section 4.3.2 that a path parameter that starts with an asterisk, such as *file, matches the rest of the URL path, including forward slashes.

Using an asset’s reverse route

In section 4.5, we saw how to use reverse routing to avoid hardcoding your application’s internal URLs. Because Assets.at is a normal controller action, it also has a reverse route that you can use in your template:

<link href="@routes.Assets.at("images/favicon.png")"
      rel="shortcut icon" type="image/png">

This results in the same href="/assets/images/favicon.png" attribute as before. Note that we don’t specify a value for the action’s path parameter, so we’re using the default. But if you had declared a second assets route, you’d have to provide the path parameter value explicitly:

<link href="@routes.Assets.at("/public/images", "favicon.png")"
      rel="shortcut icon" type="image/png">
Caching and ETags

In addition to reverse routing, another benefit of using the assets controller is its built-in caching support, using an HTTP Entity Tag (ETag). This allows a web client to make conditional HTTP requests for a resource so that the server can tell the client it can use a cached copy instead of returning a resource that hasn’t changed.

For example, if we send a request for the favorites icon, the assets controller calculates an ETag value and adds a header to the response:

Etag: 978b71a4b1fef4051091b31e22b75321c7ff0541

The ETag header value is a hash of the resource file’s name and modification date. Don’t worry if you don’t know about hashes: all you need to know is that if the file on the server is updated, with a new version of a logo for example, this value will change.

Once it has an ETag value, an HTTP client can make a conditional request, which means “only give me this resource if it hasn’t been modified since I got the version with this ETag.” To do this, the client includes the ETag value in a request header:

If-None-Match: 978b71a4b1fef4051091b31e22b75321c7ff0541

When this header is included in the request, and the favicon.png file hasn’t been modified (it has the same ETag value), then Play’s assets controller will return the following response, which means “you can use your cached copy”:

HTTP/1.1 304 Not Modified
Content-Length: 0
Compressing assets with gzip

An eternal issue in web development is how long it takes to load a page. Bandwidth may tend to increase from one year to the next, but people increasingly access web applications in low-bandwidth environments using mobile devices. Meanwhile, page sizes keep increasing due to factors like the use of more and larger JavaScript libraries in the web browser.

HTTP compression is a feature of modern web servers and web clients that helps address page sizes by sending compressed versions of resources over HTTP. The benefit of this is that you can significantly reduce the size of large text-based resources, such as JavaScript files. Using gzip to compress a large minified JavaScript file may reduce its size by a factor of two or three, significantly reducing bandwidth usage. This compression comes at the cost of increased processor usage on the client, which is usually less of an issue than bandwidth.

The way this works is that the web browser indicates that it can handle a compressed response by sending an HTTP request header such as Accept-Encoding: gzip that specifies supported compression methods. The server may then choose to send a compressed response whose body consists of binary data instead of the usual plain text, together with a response header that specifies this encoding, such as

Content-Encoding: gzip

In Play, HTTP compression is transparently built into the assets controller, which can automatically serve a compressed version of a static file, if it’s available, and if gzip is supported by the HTTP client. This happens when all of the following are true:

  • Play is running in prod mode (production mode is explained in chapter 9); HTTP compression isn’t expected to be used during development.
  • Play receives a request that’s routed to the assets controller.
  • The HTTP request includes an Accept-Encoding: gzip header.
  • The request maps to a static file, and a file with the same name but with an additional .gz suffix is found.

If any one of these conditions isn’t true, the assets controller serves the usual (uncompressed) file.

For example, suppose our application includes a large JavaScript file at public/javascripts/ui.js that we want to compress when possible. First, we need to make a compressed copy of the file using gzip on the command line (without removing the uncompressed file):

gzip --best < ui.js > ui.js.gz

This should result in a ui.js.gz file that’s significantly smaller than the original ui.js file.

Now, when Play is running in prod mode, a request for /assets/javascripts/ui.js that includes the Accept-Encoding: gzip header will result in a gzipped response.

To test this on the command line, start Play in prod mode using the play start command, and then use cURL on the command line to send the HTTP request:

curl --header "Accept-Encoding: gzip" --include
[CA] http://localhost:9000/assets/javascripts/ui.js

You can see from the binary response body and the Content-Encoding header that the response is compressed.

4.7. Summary

In this chapter, we showed you how Play implements its model-view-controller architecture and how Play processes HTTP requests. This architecture is designed to support declarative application URL scheme design and type-safe HTTP parameter mapping.

Request processing starts with the HTTP routing configuration that determines how the router processes request parameters and dispatches the request to a controller. First, the router uses the binder to convert HTTP request parameters to strongly typed Scala objects. Then the router maps the request URL to a controller action invocation, passing those Scala objects as arguments.

Meanwhile, Play uses the same routing configuration to generate reverse controllers that you can use to refer to controller actions without having to hardcode URLs in your application.

This chapter didn’t describe HTML form validation—using business rules to check request data. This responsibility of your application’s controllers is described in detail in chapter 7.

Response processing, after a request has been processed, means determining the HTTP response’s status code, headers, and response body. Play provides controller helper functions that simplify the task of generating standard responses, as well as giving you full control over status codes and headers. Using templates to generate a dynamic response body, such as an HTML document, is described in chapter 6.

In Play, this request and response processing comes together in a Scala HTTP API that combines the convenience for common cases with the flexibility to handle more complex or unusual cases, without attempting to avoid HTTP features and concepts. In the next chapter, we’ll switch from the application’s HTTP front-end interface to look at how you can implement a back-end interface to a database.

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

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