RESTful sur Play! cadre

117

Nous prévoyons un projet servant principalement du contenu aux applications mobiles, mais nous avons besoin d'un site Web.

Ma question est de savoir s'il est judicieux d'utiliser Jersey ou Restlet pour développer des API REST pour nos applications mobiles, puis d'utiliser Play! pour servir le site Web.

Ou est-il plus logique d'utiliser simplement Play! pour tout faire? Si tel est le cas, comment faire REST avec Play! cadre?

Gary
la source

Réponses:

112

Selon la demande, une approche simple de type REST. Cela fonctionne presque de la même manière que la solution de Codemwncis, mais utilise l'en-tête Accept pour la négociation de contenu. D'abord le fichier des routes:

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

Vous ne spécifiez aucun type de contenu ici. Cela n'est à mon humble avis nécessaire que lorsque vous souhaitez avoir des URI "spéciaux" pour certaines ressources. Comme déclarer une route vers /users/feed/pour toujours revenir dans Atom / RSS.

Le contrôleur d'application ressemble à ceci:

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);
}

Comme vous pouvez le voir, j'ai seulement supprimé la méthode getUserJSON et renommé la méthode getUser. Pour que différents types de contenu fonctionnent, vous devez maintenant créer plusieurs modèles. Un pour chaque type de contenu souhaité. Par exemple:

user.xml:

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

user.json:

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

user.html:

<html>...</html>

Cette approche donne aux navigateurs toujours la vue HTML, puisque tous les navigateurs envoient un type de contenu texte / html dans leur en-tête Accept. Tous les autres clients (éventuellement certaines requêtes AJAX basées sur JavaScript) peuvent définir leur propre type de contenu souhaité. En utilisant la méthode jQuerys ajax (), vous pouvez effectuer les opérations suivantes:

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

Ce qui devrait vous donner les détails sur l'utilisateur avec l'ID 1 au format JSON. Play prend actuellement en charge HTML, JSON et XML de manière native, mais vous pouvez facilement utiliser un type différent en suivant la documentation officielle ou en utilisant le module de négociation de contenu .

Si vous utilisez Eclipse pour le développement, je suggère d'utiliser le plugin client REST qui vous permet de tester vos routes et leur type de contenu correspondant.

Seb
la source
2
Merci d'avoir publié ceci. Le jeu! les documents sont parmi les meilleurs que j'ai vus pour expliquer la structure de base des choses, mais ils manquent parfois d'exemples détaillés. La démonstration des deux approches sur le même exemple clarifie vraiment les choses.
Brad Mace
J'essaie votre exemple, je suis curieux de savoir où les données JSON publiées sont converties en classe User. par exemple, dans la fonction createUser, je trouve que newUser est nul.
Gary
2
@Gary: Peut-être avez-vous utilisé "user" au lieu de "newUser"? Le nom du contrôleur et le paramètre de formulaire doivent correspondre. J'ai créé un projet simple qui montre la méthode ci-dessus, y compris la sortie HTML / XML / JSON pour tous les utilisateurs sur github.com/sebhoss/play-user-sample
seb
Merci, je l'ai testé en utilisant curl pour envoyer une chaîne JSON, et il semble que le framework de lecture n'a pas reconnu le type de contenu application / json: groups.google.com/group/play-framework/browse_thread/thread/…
Gary
@Gary: Merci pour l'indice! Il semble qu'il soit corrigé dans la branche principale, vous pouvez essayer de le créer vous-même, puis le tester à nouveau ..
seb
68

C'est toujours une question populaire, mais les réponses les plus votées ne sont pas à jour avec la version actuelle du jeu. Voici un exemple de travail REST avec play 2.2.1:

conf / routes:

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
la source
J'aimerais aussi voir une version mise à jour de la réponse de seb, mais malheureusement, votre réponse a supprimé toute la magie .xml et .html. :-(
flaschenpost
26

Utilisez Play! pour tout faire. L'écriture de services REST dans Play est très très simple.

Premièrement, le fichier de routes facilite l'écriture de routes conformes à l'approche REST.

Ensuite, vous écrivez vos actions, dans le contrôleur, pour chaque méthode API que vous souhaitez créer.

Selon la manière dont vous souhaitez renvoyer le résultat (XML, JSON, etc.), vous pouvez utiliser quelques méthodes. Par exemple, l'utilisation de la méthode renderJSON permet de rendre les résultats très facilement. Si vous souhaitez rendre XML, vous pouvez simplement le faire de la même manière que vous créeriez un document HTML dans votre vue.

Voici un bel exemple.

fichier de routes

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

Dossier de candidature

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);
}

Fichier getUser.xml

<user>
   <name>${user.name}</name>
   <dob>${user.dob}</dob>
   .... etc etc
</user>
Codemwnci
la source
Est-il possible de choisir la bonne méthode getUser en fonction de l'en-tête Accept?
Timo Westkämper
il est, mais pas entièrement fiable. Si play sait que l'en-tête est une requête JSON, il essaiera de restituer un fichier getuser.json. Si l'en-tête est un xml, alors il essaiera getuser.xml. Cependant, il est beaucoup plus facile à comprendre, et plus comme REST, à user / User / {id} / type
Codemwnci
29
Je ne pense pas qu'il soit plus semblable à REST de spécifier le type de représentation explicitement dans l'URI. Il est préférable d'utiliser directement l'en-tête Accept et de ne pas modifier l'URI car la ressource que vous souhaitez voir reste la même. L'exemple ci-dessus pourrait être réécrit pour n'avoir qu'une seule méthode getUser (Long id) qui fait exactement la même chose que son implémentation actuelle, mais au lieu de définir un getUserJSON, getUserXML, etc., vous définissez plutôt un modèle getUser.json et getUser.xml. Bien que je renomme cela en user.json / user.xml aussi
seb
Merci, ceci est très utile. Appréciez-le!
Gary
1
@seb - pouvez-vous développer votre commentaire en réponse? J'aimerais voir un exemple de la technique que vous décrivez
Brad Mace
5

L'intégration avec une implémentation JAX-RS est une approche alternative possible à l'utilisation du routage HTTP intégré de Play. Pour un exemple RESTEasy, voir le RESTEasy Play! module .

Cette approche est logique si vous êtes déjà investi dans JAX-RS ou si vous avez besoin de certaines des fonctionnalités avancées REST fournies par JAX-RS, telles que la négociation de contenu. Sinon, il serait plus simple d'utiliser simplement Play directement pour servir JSON ou XML en réponse aux requêtes HTTP.

Peter Hilton
la source
2

Il semble que cette approche soit interrompue dans Play version 1.2.3. Si vous téléchargez la source effectuée par @seb et mentionnée précédemment https://github.com/sebhoss/play-user-sample , la création d'un nouvel objet utilisateur en utilisant POST avec un objet JSON n'est plus possible.

Vous devez disposer de méthodes spécifiques pour la création à l'aide de POST json et xml. Décrit ici: https://groups.google.com/forum/#!topic/play-framework/huwtC3YZDlU

tchristensen
la source