¡DESCANSO en el juego! marco de referencia

117

Estamos planeando un proyecto que sirva principalmente contenido a aplicaciones móviles, pero necesitamos tener un sitio web.

Mi pregunta es si tiene sentido usar Jersey o Restlet para desarrollar API REST para nuestras aplicaciones móviles y luego usar Play! para servir el sitio web.

¿O tiene más sentido usar Play! para hacerlo todo? Si es así, ¿cómo hacer DESCANSO con Play! ¿marco de referencia?

Gary
fuente

Respuestas:

112

Según la solicitud, un enfoque simple similar a REST. Funciona casi de la misma manera que la solución de Codemwncis, pero usa el encabezado Accept para la negociación de contenido. Primero el archivo de rutas:

GET     /user/{id}            Application.user
POST    /user/                Application.createUser
PUT     /user/{id}            Application.updateUser
DELETE  /user/{id}            Application.deleteUser

Aquí no especificas ningún tipo de contenido. En mi humilde opinión, hacerlo solo es necesario cuando desea tener URI "especiales" para ciertos recursos. Como declarar una ruta para /users/feed/regresar siempre en Atom / RSS.

El controlador de la aplicación tiene este aspecto:

public static void createUser(User newUser) {
    newUser.save();
    user(newUser.id);
}

public static void updateUser(Long id, User user) {
    User dbUser = User.findById(id);
    dbUser.updateDetails(user); // some model logic you would write to do a safe merge
    dbUser.save();
    user(id);
}

public static void deleteUser(Long id) {
    User.findById(id).delete();
    renderText("success");
}

public static void user(Long id)  {
    User user = User.findById(id)
    render(user);
}

Como puede ver, solo eliminé el método getUserJSON y cambié el nombre del método getUser. Para que funcionen diferentes tipos de contenido, ahora debe crear varias plantillas. Uno para cada tipo de contenido deseado. Por ejemplo:

usuario.xml:

<users>
  <user>
    <name>${user.name}</name>
    . . .
  </user>
</users>

user.json:

{
  "name": "${user.name}",
  "id": "${user.id}",
  . . . 
}

user.html:

<html>...</html>

Este enfoque proporciona a los navegadores siempre la vista HTML, ya que todos los navegadores envían un tipo de contenido de texto / html en su encabezado Aceptar. Todos los demás clientes (posiblemente algunas solicitudes AJAX basadas en JavaScript) pueden definir su propio tipo de contenido deseado. Usando el método jQuerys ajax (), puede hacer lo siguiente:

$.ajax({
  url: @{Application.user(1)},
  dataType: json,
  success: function(data) {
    . . . 
  }
});

Lo que debería brindarle los detalles sobre el usuario con el ID 1 en formato JSON. Actualmente, Play admite HTML, JSON y XML de forma nativa, pero puede usar fácilmente un tipo diferente siguiendo la documentación oficial o utilizando el módulo de negociación de contenido .

Si está utilizando Eclipse para el desarrollo, le sugiero que use el complemento de cliente REST que le permite probar sus rutas y su tipo de contenido correspondiente.

seb
fuente
2
Gracias por publicar esto. ¡El juego! Los documentos son algunos de los mejores que he visto para explicar la estructura básica de las cosas, pero en ocasiones carecen de ejemplos detallados. Tener los dos enfoques demostrados en el mismo ejemplo realmente aclara las cosas.
Brad Mace
Estoy probando su ejemplo, tengo curiosidad por saber dónde se convierten los datos JSON publicados en la clase de usuario. por ejemplo, dentro de la función createUser encuentro que newUser es nulo.
Gary
2
@Gary: ¿Quizás usaste "usuario" en lugar de "newUser"? El nombre del controlador y el parámetro de formulario deben coincidir. Creé un proyecto simple que muestra el método anterior, incluida la salida HTML / XML / JSON para todos los usuarios en github.com/sebhoss/play-user-sample
seb
Gracias, lo probé usando curl para enviar una cadena JSON, y parece que el marco de juego no reconoció el tipo de contenido application / json: groups.google.com/group/play-framework/browse_thread/thread/…
Gary
@Gary: ¡Gracias por la pista! Parece que está arreglado en la rama maestra, puede intentar construirlo usted mismo y luego probar nuevamente.
seb
68

Esta sigue siendo una pregunta popular, pero las respuestas más votadas no están actualizadas con la versión actual del juego. Aquí hay un ejemplo de REST funcional con play 2.2.1:

conf / rutas:

GET     /users                 controllers.UserController.getUsers
GET     /users/:id             controllers.UserController.getUser(id: Long)
POST    /users                 controllers.UserController.createUser
PUT     /users/:id             controllers.UserController.updateUser(id: Long)
DELETE  /users/:id             controllers.UserController.deleteUser(id: Long)

app / controllers / UserController.java:

public static Result getUsers()
{
    List<User> users = Database.getUsers();
    return ok(Json.toJson(users));
}

public static Result getUser(Long id)
{
    User user = Database.getUser(id);
    return user == null ? notFound() : ok(Json.toJson(user));
}

public static Result createUser()
{
    User newUser = Json.fromJson(request().body().asJson(), User.class);
    User inserted = Database.addUser(newUser);
    return created(Json.toJson(inserted));
}

public static Result updateUser(Long id)
{
    User user = Json.fromJson(request().body().asJson(), User.class);
    User updated = Database.updateUser(id, user);
    return ok(Json.toJson(updated));
}

public static Result deleteUser(Long id)
{
    Database.deleteUser(id);
    return noContent(); // http://stackoverflow.com/a/2342589/1415732
}
Alden
fuente
También me gustaría ver una versión actualizada de la respuesta de seb, pero desafortunadamente su respuesta eliminó toda la magia .xml y .html. :-(
flaschenpost
26

Utilice Play! para hacerlo todo. Escribir servicios REST en Play es muy, muy fácil.

En primer lugar, el archivo de rutas facilita la escritura de rutas que se ajustan al enfoque REST.

Luego, escribe sus acciones, en el controlador, para cada método de API que desea crear.

Dependiendo de cómo desee devolver el resultado (XML, JSON, etc.), existen algunos métodos que puede utilizar. Por ejemplo, el uso del método renderJSON permite que los resultados se rendericen con mucha facilidad. Si desea representar XML, puede hacerlo de la misma manera que crearía un documento HTML en su Vista.

He aquí un buen ejemplo.

archivo de rutas

GET     /user/{id}            Application.getUser(format:'xml')
GET     /user/{id}/json       Application.getUserJSON
POST    /user/                Application.createUser
PUT     /user/{id}            Application.updateUser
DELETE  /user/{id}            Application.deleteUser

Archivo de aplicación

public static void createUser(User newUser) {
    newUser.save();
    renderText("success");
}

public static void updateUser(Long id, User user) {
    User dbUser = User.findById(id);
    dbUser.updateDetails(user); // some model logic you would write to do a safe merge
    dbUser.save();
    renderText("success");
}

public static void deleteUser(Long id) {
    // first check authority
    User.findById(id).delete();
    renderText("success");
}

public static void getUser(Long id)  {
    User user = User.findById(id)
    renderJSON(user);
}

public static void getUserJSON(Long id) {
    User user = User.findById(id)
    renderJSON(user);
}

archivo getUser.xml

<user>
   <name>${user.name}</name>
   <dob>${user.dob}</dob>
   .... etc etc
</user>
Codemwnci
fuente
¿Es posible elegir el método getUser correcto según el encabezado Accept?
Timo Westkämper
lo es, pero no del todo confiable. Si el juego sabe que el encabezado es una solicitud JSON, intentará generar un archivo getuser.json. Si el encabezado es un xml, intentará getuser.xml. Sin embargo, es mucho más fácil de entender, y más parecido a REST, para el usuario / Usuario / {id} / tipo
Codemwnci
29
No creo que sea más parecido a REST especificar el tipo de representación explícitamente en el URI. Es mejor usar el encabezado Accept directamente y no cambiar el URI ya que el recurso que desea ver sigue siendo el mismo. El ejemplo anterior podría reescribirse para tener un solo método getUser (Long id) que hace exactamente lo mismo que su implementación actual, pero en lugar de definir un getUserJSON, getUserXML, etc., prefiere definir una plantilla getUser.json y getUser.xml. Aunque también cambiaría el nombre de eso a user.json / user.xml
seb
Gracias, esto es muy útil. ¡Lo aprecio!
Gary
1
@seb: ¿puedes expandir tu comentario a una respuesta? Me encantaría ver un ejemplo de la técnica que describe
Brad Mace
5

La integración con una implementación de JAX-RS es un posible enfoque alternativo al uso del enrutamiento HTTP integrado de Play. Para ver un ejemplo de RESTEasy, consulte RESTEasy Play! módulo .

Este enfoque tiene sentido si ya ha invertido en JAX-RS, o si necesita algunas de las funciones avanzadas REST que JAX-RS proporciona, como la negociación de contenido. De lo contrario, sería más sencillo utilizar Play directamente para servir JSON o XML en respuesta a las solicitudes HTTP.

Peter Hilton
fuente