¡La versión 2.0 de Play ya está lista! Ayúdanos a traducir la documentación de la útlima versión y sigue nuestro progreso.

Manuales, tutoriales & referencias

Consulte

Contenidos

Elija la versión

Buscar

Busque con google

Libros

Rutas HTTP

El ‘router’ es el componente encargado de traducir los pedidos HTTP a llamadas a nuestros métodos de acción (métodos públicos, estáticos, de un controlador).

Cada pedido HTTP genera un evento que es atendido por el framework MVC. El evento contiene la siguiente información:

  • El URL completo del pedido HTTP (como /clients/1542 o /photos/list), incluyendo los parámetros del query string.
  • El método HTTP (GET, POST, PUT, DELETE)

Acerca de REST

REST (Representational state transfer) es un estilo de arquitectura de software para sistemas hipermedia distribuidos como la World Wide Web.

REST establece algunos pocos principios esenciales:

  • La funcionalidad de la aplicación se divide en recursos
  • Cada recurso es identificado unívocamente mediante una URI
  • Todos los recursos mantienen una interfaz uniforme para transferir el estado entre el cliente y el recurso.

Si está utilizando HTTP, estas interfaces consisten justamente en los métdoso HTTP. El protocolo utilizado para acceder al estado de los recursos cumple las siguientes condiciones:

  • Cliente-servidor
  • Sin estado (Stateless)
  • Cacheable
  • Dividido en capas (Layered)

Si una aplicación cumple con estos principios, se dice que la aplicación es RESTful. El framework play le facilita desarrollar este tipo de aplicaciones RESTful:

  • El router de play interpreta tanto los URI como los métodos HTTP para dirigir un pedido HTTP hacia una llamada Java. Puede incluso utilizar expresiones regulares a la hora de definir los URI para tener más flexibilidad.
  • El protocolo no mantiene estado, es ‘stateless’. Esto significa que no puede mantener ningún estado en el servidor entre dos pedidos HTTP
  • Para Play HTTP es un componente esencial del framework, por eso le brinda acceso completo a la información del pedido HTTP.

La sintaxis del archivo de rutas

El Router de play utiliza el archivo de configuración conf/routes. En este archivo figuran todas las rutas de la aplicación. Cada ruta consiste en un método HTTP seguido del patrón URI, asociado con una llamada a código Java.

Veamos qué aspecto tiene un archivo de definición de rutas:

GET    /clients/{id}             Clients.show           

Cada ruta comienza con un método HTTP, seguido de un patrón URI. El último elemento de una ruta es la definición de la llamada Java.

Puede agregar comentarios al archivo de rutas con el caracter #.

# Display a client
GET    /clients/{id}             Clients.show           

El método HTTP

El método HTTP puede ser cualquiera de los métodos válidos soportado por HTTP:

  • GET
  • POST
  • PUT
  • DELETE
  • HEAD

También soporta WS como método de acción para indicar un pedido proveniente de un WebSocket.

Si especifica * como método, la ruta será utilizada para el pedido HTTP cualquiera sea su método.

*   /clients/{id}             Clients.show           

Esta ruta será utilizada para cualquiera de los siguientes pedidos HTTP:

GET /clients/1541
PUT /clients/1212

El patrón URI

El patrón URI define el camino de la ruta del pedido HTTP. Algunas partes de dicho camino pueden ser dinámicas. Toda parte dinámica debe ser especificada entre corchetes {…}.

/clients/all

Coincidirá exactamente con:

/clients/all

pero…

/clients/{id}

Conincidirá con cualquiera de los siguientes URIs:

/clients/12121
/clients/toto

Un patrón URI puede tener más de una parte dinámica:

/clients/{id}/accounts/{accountId}

La estrategia por defecto utilizada para completar las partes dinámicas responde a la siguiente expresión regular /[^/]+/. Puede definir su propia expresion regular para cada parte dinámica.

Por ejemplo, esta expresión regular tan sólo aceptará valores númericos para el parámetro id:

/clients/{<[0-9]+>id}

Mientras que con ésta nos aseguraremos que id sea una palabra de entre 4 y 10 caracteres en minúscula:

/clients/{<[a-z]{4,10}>id}

Puede utilizar cualquier expresión regular válida.

Nota

Las partes dinámicas reciben un nombre. El controlador puede acceder a la parte dinámica de las rutas a través del params map.

Por defecto, Play considera que las barras finales de los URLs son significativos. Por ejemplo, la siguiente ruta:

GET     /clients         Clients.index

coincidirá con /clients pero no con /clients/. Para indicarle a Play que la barra final es opcional, agregue un signo de pregunta al final del patrón URI, de la siguiente manera:

GET     /clients/?       Clients.index

El patrón URI no puede tener ninguna parte opcional, a excepción de la barra del final.

Definición de la llamada Java

La última parte de la definición de una ruta es la llamada Java. Esta parte es definida por el nombre completo de un método de accion. La acción debe ser un método public static void pertenenciente a controlador. A su vez, la clase del controlador debe estar definida en el paquete controllers y debe ser una subclase de play.mvc.Controller.

Puede especificar un paquete de Java antes del nombre de la clase del controlador si no está definida directamente en el paquete controllers. El paquete controllers está definido de manera implícita, por lo que no necesita especificarlo.

GET    /admin             admin.Dashboard.index           

404 como una acción

Puede directamente utilizar 404 como una acción en el archivo de rutas para especifcar un camino URL que debe ser ignorado por la aplicación, por ejemplo:

# Ignore favicon requests
GET     /favicon.ico            404

Asignando argumentos estáticos

En algunod casos, puede querer reutilizar una acción ya existente, pero dejando fijo algún parámetro dinámico.

Veamos un ejemplo, tenemos el siguiente método de acción:

public static void page(String id) {
    Page page = Page.findById(id);
    render(page);
}

Con su correspondiente ruta:

GET    /pages/{id}        Application.page

Ahora quiero definir un alias para la página con el id ‘home’. Puedo entonces definir otra ruta con un parámetro estático:

GET    /home              Application.page(id:'home')
GET    /pages/{id}        Application.page

La primera ruta es equivalente a la segunda cuando el parámetro ID es ‘home’. Sin embargo, dado que tiene una prioridad más alta, esta ruta será utilizada para la llamada Application.page cuando el ID es ‘home’.

Variables y scripts

También puede utilizar la sintaxis ${…} para insertar variables y %{…} para insertar scripts de groovy en el archivo @routes, tal como sucede con los templates. Por ejemplo:

%{ context = play.configuration.getProperty('context', '') }%
 
# Home page
GET    ${context}         Secure.login
GET    ${context}/        Secure.login

Otro ejemplo que puede estudiar, es el archivo de rutas del módulo CRUD, el cual utiliza el tag crud.types para recorrer los modelos de su aplicación y generar la definición de las rutas del controlador para cada modelo.

Prioridad de las rutas

Muchas rutas pueden coincidir con un mismo pedido HTTP. Si hay algún conflicto, se utilizará la primera ruta (de acuerdo al orden en que aparecen en el archivo conf/routes)

Por ejemplo:

GET    /clients/all       Clients.listAll
GET    /clients/{id}      Clients.show

Con esta definición, la URI:

/clients/all

será interceptada por la primera ruta, y llamará a Clients.listAll (aún cuando la segunda ruta también coincidiría con ese URI).

Configurando recursos estáticos

staticDir: carpeta

Utilice la acción staticDir, para apuntar a cada carpeta que desea publicar como contenedora de recursos estáticos.

Por ejemplo:

GET    /public/           staticDir:public

Cuando reciba una pedido HTTP para /public/*, Play retornará los archivos de la carpeta /public.

Las prioridades se rigen por el mismo criterio que las rutas estándar.

staticFile: archivo

También puede especificar un camino URL a un archivo estático específico.

# Serve index.html static file for home requests
GET     /home                   staticFile:/public/html/index.html

Codificación de URL

Dado que es imposible decodificar y volver a codificar un URL (no hay manera de saber si un “/” es un “/” o un “%2F”, por poner un ejemplo), las URL deben ser expresadas de manera codificada. La codificación por defecto es UTF-8, pero puede utilizar ISO-8859-1, UTF-16BE, UTF-16LE, UTF-16, utilizando el parámetro de configuración defaultWebEncoding. Vea http://download.oracle.com/javase/1.4.2/docs/api/java/nio/charset/Charset.html para más información.

Por ejemplo:

# map /stéphane
GET /st%C3%A9phane Application.stephane

Inversión de rutas: generando URLs a partir de métodos de acción

El Router puede ser utilizado también para generar URLs a partir de llamadas Java. De esta manera, puede concentrar en un único archivo todos sus patrones URI, y luego quedarse tranquilo a la hora de hacer un refactoring de su aplicación.

Por ejemplo, con esta definición de ruta:

GET    /clients/{id}      Clients.show

Desde su código, puede generar un URL capaz de invocar Clients.show de la siguente manera:

map.put("id", 1541);
String url = Router.reverse("Clients.show", map).url;// GET /clients/1541

La generación de URLs está integrada en buena parte de los componentes del framework. No debería tener necesidad de utilizar la operación Router.reverse de manera directa.

Si agrega parámetros que no están incluidos en el patrón URI, estos parámetros serán agregados al query string:

map.put("id", 1541);
map.put("display", "full");
// GET /clients/1541?display=full
String url = Router.reverse("Clients.show", map).url;

Una vez más, Play utilizará la prioridad de las rutas para encontrar la ruta más específica capaz de generar el URL.

Estableciendo los tipos de contenido (content-type)

Play selecciona un media type para la respuesta HTTP de acuerdo al valor del request.format. Este valor determina el template a utilizar, mediante la extensión del archivo, y también determina el Content-type de la respuesta, de acuerdo al tipo de contenido especificado en el archivo mime-types.properties para ese formato.

El formato por defecto para un pedido de Play es html. Por lo tanto, el template por defecto para el método index() de un controlador (con formato @html) es el archivo index.html. Si especifica un formato diferente, puede elegir un template distinto a ser utilizado para generar la respuesta HTTP.

Puede establecer el formato programáticamente antes de llamar al método render. Por ejemplo, para devolver un archivo CSS, de Hojas de estilo en cascada, con el tipo de contenido text/css, puede hacer lo siguiente:

request.format = "css";  

Sin embargo, un enfoque más limpio es utilizar el URL para especificar el formato en el archivo routes. Puede agregar formatos a rutas específicas especificando el formato para el método del controlador. Por ejemplo, la siguiente ruta coincidirá con un pedido HTTP para /index.xml, fijando el formato a xml y utilizando como template el archivo index.xml.

GET    /index.xml         Application.index(format:'xml')  

De manera similar:

GET    /stylesheets/dynamic_css   css.SiteCSS(format:'css')

También puede extraer directamente el formato de la URL, con una ruta como la siguiente:

GET    /index.{format}    Application.index 

Con esta ruta, un pedido para /index.xml establecerá como formato xml y utilizará el template XML, mientras que /index.txt utilizará el template de texto plano.

Play también puede establecer el formato de manera automática, utilizando negociación de contenidos (HTTP content negotiation).

Negociación de contenidos (HTTP content negotiation)

Una cosa que Play tiene con común con otras arquitecturas RESTful es que brinda acceso directo a la funcionalidad HTTP, en vez de intentar ocultarla o poner una capa de abstracción encima de ella. Content negotiation es una caracterísitca de HTTP que le permite a un servidor HTTP retornar diferentes media types para el mismo URL, según el tipo de medio especificado en el pedido HTTP. El cliente indica qué content-types son aceptables, especificando los media types en el header Accept del pedido HTTP. Por ejemplo, para solicitar una respuesta en formato XML, utilizaría el siguiente header:

Accept: application/xml

Un cliente HTTP puede especificar más de un media type, e incluso especificar que cualquier tipo de media type es aceptable con el siguiente catch-all media type (*/*)

Accept: application/xml, image/png, */*

Los exploradores web convencionales siempre incluyen este media type catch-all en el Accept header: aceptarán cualquier media type, y Play retornará HTML, el formato por defecto. La negociación de contenido es comúnmente utilizada por clientes que requieren una respuesta en un formato específico, como puede ser una llamada AJAX que requiere una respuesta en formato JSON, o un lector de e-books que requiere la versión PDF o EPUB de un documento.

Estableciendo el content type desde los headers HTTP

Play elige el formato por defecto, html, si el header Accept contiene text/html o application/xhtml, o si figura el valor */*. El formato por defecto no es seleccionado si no figura el valor catch-all, */*.

Play incluye soporte para algunos formatos de uso común: html, txt, json y xml. Por ejemplo, definamos un método de acción que despliegue alguna información:

public static void index() { 
   final String name = "Peter Hilton"; 
   final String organisation = "Lunatech Research"; 
   final String url = "http://www.lunatech-research.com/"; 
   render(name, organisation, url); 
} 

Si ingresa un URL que está asociado a este método (http://localhost:9000/ en una aplicación nueva de Play) en un explorador web, play utilizará el template index.html, ya que los exploradores web típicamente envían un Accept header que incluye el valor text/html.

Play responde a un pedido HTTP con el header Accept: text/xml estableciendo como formato xml y utilizando el template index.xml, como en el siguiente caso:

<?xml version="1.0"?> 
<contact> 
<name>${name}</name> 
<organisation>${organisation}</organisation> 
<url>${url}</url> 
</contact> 

El sistema mediante el cual play determina qué template utilizar funciona de la siguiente manera. Para un método de acción index(), el header Accept contiene un media type que Play relaciona con un formato específico, el cual a su vez es vinculado con un archivo de template.

Accept header Format Template file name Mapping
null null index.html Default template extension for null format
image/png null index.html Media type not mapped to a format
*/*, image/png html index.html Default media type mapped to html format
text/html html index.html Built-in format
application/xhtml html index.html Built-in format
text/xml xml index.xml Built-in format
application/xml xml index.xml Built-in format
text/plain txt index.txt Built-in format
text/javascript json index.json Built-in format
application/json, */* json index.json Built-in format, default media type ignored

Formatos customizados

Puede agregar negociación de contenidos para sus propios tipos customizados, inspeccionado los headers del pedido HTTP y estableciendo el formato correspondiente, de manera tal que solamente utilice este formato cuando el pedido HTTP requiera el correspondiente media type. Por ejemplo, para retornar un vCard con media type text/x-vcard, verifique en el controlador si el cliente está requiriendo ese media type, y establezca el formato correspondiente antes de cada pedido HTTP, de la siguiente manera:

@Before 
static void setFormat() { 
	if (request.headers.get("accept").value().equals("text/x-vcard")) { 
		request.format = "vcf"; 
	} 
} 

De esta manera, un pedido HTTP con un header Accept: text/x-vcard, utilizará un template index.vcf como el siguiente:

BEGIN:VCARD 
VERSION:3.0 
N:${name} 
FN:${name} 
ORG:${organisation} 
URL:${url} 
END:VCARD  

Próximos pasos

Una vez que el Router ha determinado qué método de Java invocar para el pedido HTTP recibido, el framework se encarga de efectuar la llamada a Java. Veamos ahora cómo funcionan los Controladores.