Actions

We have talked about routes, and how to pass parameters to controllers. Let's now talk about what we can do with the controller.

The method defined in the route must return a play.api.mvc.Action instance. The Action type is a thin wrapper around the type Request[A] => Result, where Request[A] identifies an HTTP request and Result is an HTTP response.

Composing the response

An HTTP response, as we saw in Chapter 7, Web APIs, is composed of:

  • the status code (such as 200 for a successful response, or 404 for a missing page)
  • the response headers, a key-value list indicating metadata related to the response
  • The response body. This can be HTML for web pages, or JSON, XML or plain text (or many other formats). This is generally the bit that we are really interested in.

The Play framework defines a play.api.mvc.Result object that symbolizes a response. The object contains a header attribute with the status code and the headers, and a body attribute containing the body.

The simplest way to generate a Result is to use one of the factory methods in play.api.mvc.Results. We have already seen the Ok method, which generates a response with status code 200:

def hello(name:String) = Action {
  Ok("hello, $name")
}

Let's take a step back and open a Scala console so we can understand how this works:

$ activator console
scala> import play.api.mvc._
import play.api.mvc._

scala> val res = Results.Ok("hello, world")
res: play.api.mvc.Result = Result(200, Map(Content-Type -> text/plain; charset=utf-8))

scala> res.header.status
Int = 200

scala> res.header.headers
Map[String,String] = Map(Content-Type -> text/plain; charset=utf-8)

scala> res.body
play.api.libs.iteratee.Enumerator[Array[Byte]] = play.api.libs.iteratee.Enumerator$$anon$18@5fb83873

We can see how the Results.Ok(...) creates a Result object with status 200 and (in this case), a single header denoting the content type. The body is a bit more complicated: it is an enumerator that can be pushed onto the output stream when needed. The enumerator contains the argument passed to Ok: "hello, world", in this case.

There are many factory methods in Results for returning different status codes. Some of the more relevant ones are:

  • Action { Results.NotFound }
  • Action { Results.BadRequest("bad request") }
  • Action { Results.InternalServerError("error") }
  • Action { Results.Forbidden }
  • Action { Results.Redirect("/home") }

For a full list of Result factories, consult the API documentation for Results (https://www.playframework.com/documentation/2.4.x/api/scala/index.html#play.api.mvc.Results).

We have, so far, been limiting ourselves to passing strings as the content of the Ok result: Ok("hello, world"). We are not, however, limited to passing strings. We can pass a JSON object:

scala> import play.api.libs.json._
import play.api.libs.json._

scala> val jsonObj = Json.obj("hello" -> "world")
jsonObj: play.api.libs.json.JsObject = {"hello":"world"}

scala> Results.Ok(jsonObj)
play.api.mvc.Result = Result(200, Map(Content-Type -> application/json; charset=utf-8))

We will cover interacting with JSON in more detail when we start building the API. We can also pass HTML as the content. This is most commonly the case when returning a view:

scala> val htmlObj = views.html.index("hello")
htmlObj: play.twirl.api.HtmlFormat.Appendable =

<!DOCTYPE html>

<html lang="en">
    <head>
...

scala> Results.Ok(htmlObj)
play.api.mvc.Result = Result(200, Map(Content-Type -> text/html; charset=utf-8))

Note how the Content-Type header is set based on the type of content passed to Ok. The Ok factory uses the Writeable type class to convert its argument to the body of the response. Thus, any content type for which a Writeable type class exists can be used as argument to Ok. If you are unfamiliar with type classes, you might want to read the Looser coupling with type classes section in Chapter 5, Scala and SQL through JDBC.

Understanding and parsing the request

We now know how to formulate (basic) responses. The other half of the equation is the HTTP request. Recall that an Action is just a function mapping Request => Result. We can access the request using:

def hello(name:String) = Action { request => 
  ...
}

One of the reasons for needing a reference to the request is to access parameters in the query string. Let's modify the Hello, <name> example that we wrote earlier to, optionally, include a title in the query string. Thus, a URL could be formatted as /hello/Jim?title=Dr. The request instance exposes the getQueryString method for accessing specific keys in the query string. This method returns Some[String] if the key is present in the query, or None otherwise. We can re-write our hello controller as:

def hello(name:String) = Action { request =>
  val title = request.getQueryString("title")
  val titleString = title.map { _ + " " }.getOrElse("")
  Ok(s"Hello, $titleString$name")
}

Try this out by accessing the URL 127.0.0.1:9000/hello/Odersky?title=Dr in your browser. The browser should display Hello, Dr Odersky.

We have, so far, been concentrating on GET requests. These do not have a body. Other types of HTTP request, most commonly POST requests, do contain a body. Play lets the user pass body parsers when defining the action. The request body will be passed through the body parser, which will convert it from a byte stream to a Scala type. As a very simple example, let's define a new route that accepts POST requests:

POST      /hello            controllers.Application.helloPost

We will apply the predefined parse.text body parser to the incoming request body. This converts the body of the request to a string. The helloPost controller looks like:

def helloPost = Action(parse.text) { request =>
  Ok("Hello. You told me: " + request.body)
}

Tip

You cannot test POST requests easily in the browser. You can use cURL instead. cURL is a command line utility for dispatching HTTP requests. It is installed by default on Mac OS and should be available via the package manager on Linux distributions. The following will send a POST request with "I think that Scala is great" in the body:

$ curl --data "I think that Scala is great" --header "Content-type:text/plain"  127.0.0.1:9000/hello

This prints the following line to the terminal:

Hello. You told me: I think that Scala is great

There are several types of built-in body parsers:

  • parse.file(new File("filename.txt")) will save the body to a file.
  • parse.json will parse the body as JSON (we will learn more about interacting with JSON in the next section).
  • parse.xml will parse the body as XML.
  • parse.urlFormEncoded will parse the body as returned by submitting an HTML form. The request.body attribute is a Scala map from String to Seq[String], mapping each form element to its value(s).

For a full list of body parsers, the best source is the Scala API documentation for play.api.mvc.BodyParsers.parse available at: https://www.playframework.com/documentation/2.5.x/api/scala/index.html#play.api.mvc.BodyParsers$parse$.

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

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