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