Introducción a Play Framework 2 (Parte III: El Controlador)

En esta serie introductoria a Play 2 Framework con Scala hemos hecho una muy breve introducción al lenguaje de programación Scala, además de descubrir como se crea el modelo de datos en una aplicación desarrollada con Play 2 Framework.

En esta nueva entrega vamos a descubrir como funciona el controlador. Como todos ya sabéis, Play 2 es un framework que sigue el patrón de diseño MVC ya que permite mantener sus diferentes componentes en orden y la lógica separada de la interfaz. Además, el patrón MVC está de moda en el desarrollo web desde hace ya varios años.

El Controlador

El controlador en Play 2 cumple con la misión de traducir consultas HTTP en algo entendible por el modelo de datos (que debe implementar la lógica de negocio de la aplicación). Un Controlador en Play 2 se compone de Actions y Results.

Actions

La mayoría de las peticiones recibidas por una aplicación desarrollada con Play 2 son manejadas por un Action. Un Action es básicamente una función que toma un objeto Request como parámetro y devuelve un objeto Result. Esta función maneja la petición proveniente del navegador y genera un resultado que es enviado al cliente. Por ejemplo:

def hello = Action { request =>
  Ok("Hello World!")
}

En el ejemplo anterior, Ok es un método que construye una respuesta 200 OK que contiene un cuerpo tipo text/plain. Existen muchos otros métodos que construyen respuestas "prefabricadas" a continuación el listado completo:

  • Accepted: Genera una respuesta 202 ACCEPTED

  • BadRequest: Genera una respuesta 400 BAD_REQUEST

  • Conflict: Genera una respuesta 409 CONFLICT

  • Created: Genera una respuesta 201 CREATED

  • EntityTooLarge: Genera una respuesta 413 REQUEST_ENTITY_TOO_LARGE

  • ExpectationFailed: Genera una respuesta 417 EXPECTATION_FAILED

  • Forbidden: Genera una respuesta 403 FORBIDDEN

  • Found: Genera una respuesta 302 FOUND

  • Gone: Genera una respuesta 410 GONE

  • InternalServerError: Genera una respuesta 500 INTERNAL_SERVER_ERROR

  • MethodNotAllowed: Genera una respuesta 405 METHOD_NOT_ALLOWED

  • MovedPermanently: Genera una respuesta 301 MOVED_PERMANENTLY

  • NoContent: Genera una respuesta 204 NO_CONTENT

  • NonAuthoritativeInformation: Genera una respuesta 203 NON_AUTHRITATIVE_INFORMATION

  • NotAcceptable: 406 NOT_ACCEPTABLE

  • NotFound: General una respuesta 404 NOT_FOUND

  • NotImplemented: Genera una respuesta 501 NOT_IMPLEMENTED

  • NotModified: Genera una respuesta 304 NOT_MODIFIED

  • PartialContent: Genera una respuesta 206 PARTIAL_CONTENT

  • PreconditionFailed: Genera una respuesta 412 PRECONDITION_FAILED

  • RequestTimeout: Genera una respuesta REQUEST_TIMEOUT

  • ResetContent: Genera una respuesta 205 RESET_CONTENT

  • SeeOther: Genera una respuesta 303 SEE_OTHER

  • ServiceUnavailable: Genera una respuesta 503 SERVICE_UNAVAILABLE

  • TemporaryRedirect: Genera una respuesta 307 TEMPORARY_REDIRECT

  • TooManyRequest: Genera una respuesta 429 TOO_MANY_REQUEST

  • Unauthorized: Genera una respuesta 401 UNAUTHORIZED

  • UnsupportedMediaType: Genera una respuesta UNSUPPORTED_MEDIA_TYPE

  • UriTooLong: Genera una respuesta REQUEST_URI_TOO_LONG

La idea detrás de todos estos métodos es que sean usados cuando sea necesario y se utilice el sistema de errores de HTTP de forma coherente. Por ejemplo podemos utilizar el método BadRequest de la siguiente forma:

def saluda = Action { implicit request =>
  saludoForm.bindFromRequest.fold(
    formWithErrors => BadRequest(html.createForm(formWithErrors)),
    saludo => Ok("Hola %s".format(saludo.name))
  )
}

También es posible generar respuestas "simples" (y flexibles) utilizando el método SimpleResult al que le pasamos como parámetros las cabeceras de la respuesta y el body, yo hago uso de este método para devolver JSON. Un ejemplo de uso sacado de la documentación de Play 2:

def index =Action{
  SimpleResult(
    header =ResponseHeader(200,Map(CONTENT_TYPE -> "text/plain")), 
    body =Enumerator("Hello world!")
  ) }

Por supuesto lo anterior es equivalente al primer ejemplo de este artículo. Además de esto podemos utilizar sencillas redirecciones con el método Redirect al que pasaremos como parámetro la ruta a donde reenviar al navegador y si lo queremos, un estado:

def index =Action{
  Redirect("/user/home", status = MOVED_PERMANENTLY) }

El Ejemplo

En el caso de nuestro ejemplo de aplicación, el controlador sería igual de sencillo que el resto del código que hemos escrito para él en los artículos anteriores:

package controllers
import play.api.data.Form
import play.api.data.Forms.{single, nonEmptyText}
import play.api.mvc.{Action, Controller}
import anorm.NotAssigned
import models.User
object Application extends Controller { val userForm = Form( single( "firstname" -> nonEmptyText, "lastname" -> nonEmptyText, "age" -> nonEmptyText ) def index = Action { Ok(views.html.index(userForm)) } def addUser() = Action { implicit request => userForm.bindFromRequest.fold( errors => BadRequest, { val person = Person(firstname, lastname, age) User.create(User(NotAssigned, person)) Redirect(routes.Application.index()) } ) } ) }

El objeto userForm mapea los parámetros de la petición a un objeto de tipo Form que puede aplicar validación a los datos introducidos. La función addUser maneja una petición e intenta mapear los parámetros de la petición al userForm. Si el proceso falla, se devuelve una respuesta tipo BadRequest, si no, se construye un nuevo objeto User que es guardado en la base de datos y se redirije el navegador al índice.

Nótese que al declarar request como implicit estamos haciéndolo accesible a otras APIs dentro del método addUser.

Esto es todo por hoy, en el siguiente artículo hablaremos sobre el enrutamiento.

Más en Genebeta Dev | Introducción a Play Framework 2

Portada de Genbeta