Search icon
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
Mastering play framework for scala

You're reading from  Mastering play framework for scala

Product type Book
Published in May 2015
Publisher
ISBN-13 9781783983803
Pages 274 pages
Edition 1st Edition
Languages
Author (1):
Shiti Saxena Shiti Saxena
Profile icon Shiti Saxena

Table of Contents (21) Chapters

Mastering Play Framework for Scala
Credits
About the Author
Acknowledgments
About the Reviewers
www.PacktPub.com
Preface
1. Getting Started with Play 2. Defining Actions 3. Building Routes 4. Exploring Views 5. Working with Data 6. Reactive Data Streams 7. Playing with Globals 8. WebSockets and Actors 9. Testing 10. Debugging and Logging 11. Web Services and Authentication 12. Play in Production 13. Writing Play Plugins Index

Chapter 2. Defining Actions

If you're reading this, you've either survived the first chapter or skipped it. Either way, I am assuming you know the structure of a simple Play application. A controller in Play generates Action values and, to do so, it uses several objects and methods internally. In this chapter, we will see what goes on behind the scenes and how we can leverage these actions when we build our application.

In this chapter, we will be covering the following topics:

  • Defining Actions

  • Request body parsers

  • Action composition and troubleshooting

A dummy Artist model


In the following sections, we will give make reference to an artist model. It is a simple class with a companion object, defined as follows:

case class Artist(name: String, country: String)

object Artist {
  val availableArtist = Seq(Artist("Wolfgang Amadeus Mozart", "Austria"), 
    Artist("Ludwig van Beethoven", "Germany"), 
    Artist("Johann Sebastian Bach", "Germany"), 
    Artist("Frédéric François Chopin", "Poland"), 
    Artist("Joseph Haydn", "Austria"), 
    Artist("Antonio Lucio Vivaldi", "Italy"), 
    Artist("Franz Peter Schubert", "Austria"), 
    Artist("Franz Liszt", "Austria"), 
    Artist("Giuseppe Fortunino Francesco Verdi", "Austria")) 

  def fetch: Seq[Artist] = {
    availableArtist 
  } 

  def fetchByName(name: String): Seq[Artist] = {
    availableArtist.filter(a => a.name.contains(name)) 
  } 

  def fetchByCountry(country: String): Seq[Artist] = {
    availableArtist.filter(a => a.country == country) 
  } 

  def fetchByNameOrCountry...

Actions


An Action in Play defines how a server should respond to a request. The methods, which define an Action, are mapped to a request in the routes file. For example, let's define an Action which displays the information of all the artists as a response:

def listArtist = Action {
  Ok(views.html.home(Artist.fetch))
}

Now, to use this Action, we should map it to a request in the routes file.

GET     /api/artist       controllers.Application.listArtist

In this example, we fetch all the artists and send them with the view, as the response to the request.

Note

The term api used in the route file is just a URL prefix and is not mandatory.

Run the application and access http://localhost:9000/api/artist from the browser. A table with the available artist is visible.

Action takes a request and yields a result. It is an implementation of the EssentialAction trait. It is defined as:

trait EssentialAction extends (RequestHeader => Iteratee[Array[Byte], Result]) with Handler {
 
  def apply() = this

...

Request body parsers


Consider the most common POST request in any application—the request sent for logins. Will it be sufficient if the request body has the user's credentials in, say, a JSON or XML format? Will the request handler be able to extract this data and process it directly? No, since the data in the request has to be understood by the application code, it must be translated into a compatible type. For example, XML sent in a request must be translated to Scala XML for a Scala application.

There are several libraries, such as Jackson, XStream, and so on, which can be used to achieve this task, but we wouldn't need them as Play supports this internally. Play provides request body parsers to transform the request body into equivalent Scala objects for some of the frequently used content types. In addition to this, we can extend existing parsers or define new ones.

Every Action has a parser. How do I know this ? Well, the Action object, which we used to define how our app should respond...

Extending a parser


Let's extend the JSON parser so that we get a subscription model. We will assume that the Subscription model is defined as follows:

case class Subscription(emailId: String, interval: String) 

Now, let's write a parser that transforms the request body into a subscription object. The following code should be written in a controller:

val parseAsSubscription = parse.using {
    request => 
      parse.json.map {
        body => 
          val emailId:String = (body \ "emailId").as[String] 
          val fromDate:Long = (body \ "fromDate").as[Long] 
          Subscription(emailId, fromDate)
      }
  }

  implicit val subWrites = Json.writes[Subscription]
  def getSub = Action(parseAsSubscription) {
    request => 
      val subscription: Subscription = request.body
      Ok(Json.toJson(subscription))
   } 

There are also tolerant parsers. By tolerant, we mean that errors in a format are not ignored. This simply means that it ignores the content type header in the request...

Exploring the results


In Play, the response to a request is a result. A result has two components: the response header and the response body. Let's look at a simple example of this:

def plainResult = Action {
  Result( 
    header = ResponseHeader(200, Map(CONTENT_TYPE -> "text/plain")), 
    body = Enumerator("This is the response from plainResult method".getBytes())
  )
}

Notice that we used an enumerator for the response body. An enumerator is a means to provide data to an iteratee. We will discuss these in detail in Chapter 6, Reactive Data Streams.

Apart from this, a result has additional functions that equips us with better means to handle response headers, sessions, cookies, and so on.

A result can send JSON, XML, and images as a response, apart from a String content. An easier way of generating a result is to use the result helpers. A result helper is used for most of the HTTP response status. As an example, let's see how the TODO Action that comes built in with Play is implemented...

Asynchronous Actions


Suppose that we are at a food court and place an order to eat something at a kiosk, we are given a token and a bill. Later, when the order is ready, the kiosk flashes the token number, and upon noticing it, we collect the order.

This is similar to a request with an asynchronous response cycle, where the kiosk acts like the server, the order acts similar to a request, and the token as a promise, which gets resolved when the order is ready.

Most operations are better handled asynchronously. This is also mostly preferred since it does not block server resources until the operation is completed.

Play Action is a helper object, which extends the ActionBuilder trait. The apply method of the ActionBuilder trait implements the Action trait, which we saw earlier. Let's take a look at the relevant code from the ActionBuilder trait:

trait ActionBuilder[+R[_]] extends ActionFunction[Request, R] { 
  self => 

  final def apply[A](bodyParser: BodyParser[A])(block: R[A] => 
   ...

Content negotiation


According to HTTP:

Content negotiation is the process of selecting the best representation for a given response when there are multiple representations available.

It can either be server-driven or agent-driven or a combination of both, which is called transparent negotiation. Play provides support for server-driven negotiations. This is handled by the rendering trait and is extended by the controller trait. The controller trait is the one where the controller objects in a Play app extend.

Let's look at the Rendering trait:

trait Rendering {

   object render { 

    //Tries to render the most acceptable result according to the request's Accept header value. 
    def apply(f: PartialFunction[MediaRange, Result])(implicit request: RequestHeader): Result = { 
      def _render(ms: Seq[MediaRange]): Result = ms match {
        case Nil => NotAcceptable 
        case Seq(m, ms @ _*) => 
          f.applyOrElse(m, (m: MediaRange) => _render(ms)) 
      } 

      // "If...

Filters


In most applications, we need to perform the same operation for all requests. We might be required to add a few fields to all the responses at a later stage, after we have already defined all the actions needed for our application.

So, in this case, will we have to update all the Actions?

No. This is where the filter API comes to our rescue. We don't need to modify how we define our Actions to solve the problem. All we need to do is define a filter and use it.

Let's see how we can define our filter:

import org.joda.time.DateTime 
import org.joda.time.format.DateTimeFormat
import play.api.mvc._
import play.api.http.HeaderNames._ 
import play.api.libs.concurrent.Execution.Implicits.defaultContext 

object HeadersFilter {
  val noCache = Filter { 
    (nextFilter, rh) => 
      nextFilter(rh) map { 
        case result: Result => addNoCacheHeaders(result) 
      } 
  } 

  private def addNoCacheHeaders(result: Result): Result = { 
    result.withHeaders(PRAGMA -> "no-cache", 
...

Action composition


Defining an Action for a request is merely the act of using the Action helper object, which is defined as follows:

object Action extends ActionBuilder[Request] {
  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = block(request) 
}

The code which we write within an action block goes on to be the invokeBlock method. This method is inherited from ActionBuilder. This is a trait that provides helper methods to generate an Action. All the different ways in which we define an Action, such as async, synchronous, with or without specifying a parser, and so on are declared in ActionBuilder.

We can also define our custom Actions by extending ActionBuilder and defining a custom invoke block.

The need for an Action composition

Let's take a case study. A lot of applications these days keep track of requests, such as the IP address of the machine it was instigated from, the time it was received, or even the whole request as is. It would be a crime to add...

Troubleshooting


Here are the scenarios you might come across where you may need to troubleshoot:

  1. Coming across an error during compilation: you cannot find any HTTP request header here.

    You get this error even after you have defined the Action using a RequestHeader.

    Most of the methods used in Play that deal with requests, expect an implicit RequestHeader. This convention has been followed in order to keep the code simple. For example, let's look at the controller trait here:

    trait Controller extends Results with BodyParsers with HttpProtocol with Status with HeaderNames with ContentTypes with RequestExtractors with Rendering { 
    
      //Provides an empty `Action` implementation: the result is a standard 'Not implemented yet' result page. 
      val TODO = Action { 
         NotImplemented[play.api.templates.Html](views.html.defaultpages.todo()) 
      } 
    
      //Retrieves the session implicitly from the request. 
      implicit def session(implicit request: RequestHeader) = request.session 
    
      //Retrieve the flash...

Summary


In this chapter, we saw how to define and extend the key components of a controller. We saw how to define an application-specific Action with default parsers and results, as well as with custom parsers and results. In addition to this, we also saw how to manage application-specific concerns using filters and ActionComposition. In the process, we saw how to define a custom request.

lock icon The rest of the chapter is locked
You have been reading a chapter from
Mastering play framework for scala
Published in: May 2015 Publisher: ISBN-13: 9781783983803
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime}