RESTful on Play! struttura


117

Stiamo pianificando un progetto che serve principalmente contenuti alle app mobili, ma abbiamo bisogno di un sito web.

La mia domanda è se abbia senso utilizzare Jersey o Restlet per sviluppare API REST per le nostre app mobili e quindi utilizzare Play! per servire il sito web.

O ha più senso usare solo Play! fare tutto? In tal caso, come eseguire REST con Play! struttura?

Risposte:


112

Come da richiesta, un semplice approccio simile a REST. Funziona quasi allo stesso modo della soluzione di Codemwncis, ma utilizza l'intestazione Accept per la negoziazione del contenuto. Prima il file delle rotte:

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

Non specifichi alcun tipo di contenuto qui. In questo modo IMHO è necessario solo quando si desidera avere URI "speciali" per determinate risorse. Come dichiarare una rotta per /users/feed/tornare sempre in Atom / RSS.

Il controller dell'applicazione ha questo aspetto:

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

Come puoi vedere, ho rimosso solo il metodo getUserJSON e rinominato il metodo getUser. Affinché diversi tipi di contenuto funzionino, ora è necessario creare diversi modelli. Uno per ogni tipo di contenuto desiderato. Per esempio:

user.xml:

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

user.json:

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

user.html:

<html>...</html>

Questo approccio offre ai browser sempre la visualizzazione HTML, poiché tutti i browser inviano un tipo di contenuto testo / html nell'intestazione Accept. Tutti gli altri client (possibilmente alcune richieste AJAX basate su JavaScript) possono definire il proprio tipo di contenuto desiderato. Usando il metodo jQuerys ajax () puoi fare quanto segue:

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

Che dovrebbe fornirti i dettagli sull'utente con ID 1 in formato JSON. Play attualmente supporta HTML, JSON e XML in modo nativo, ma puoi facilmente utilizzare un tipo diverso seguendo la documentazione ufficiale o utilizzando il modulo di negoziazione del contenuto .

Se stai usando Eclipse per lo sviluppo ti suggerisco di usare il plugin client REST che ti permette di testare i tuoi percorsi e il loro tipo di contenuto corrispondente.


2
Grazie per aver postato questo. Il gioco! i documenti sono tra i migliori che ho visto per spiegare la struttura di base delle cose, ma a volte mancano di esempi dettagliati. Avere i due approcci dimostrati sullo stesso esempio chiarisce davvero le cose.
Brad Mace

Sto provando il tuo esempio, sono curioso di sapere dove i dati JSON pubblicati vengono convertiti nella classe User. ad esempio, all'interno della funzione createUser trovo che newUser è nullo.
Gary

2
@Gary: Forse hai usato "user" invece di "newUser"? Il nome del controller e il parametro del modulo devono corrispondere. Ho creato un semplice progetto che mostra il metodo sopra, incluso l'output HTML / XML / JSON per tutti gli utenti su github.com/sebhoss/play-user-sample
seb

Grazie, l'ho testato utilizzando curl per inviare la stringa JSON e sembra che il framework di riproduzione non abbia riconosciuto il tipo di contenuto application / json: groups.google.com/group/play-framework/browse_thread/thread/…
Gary,

@Gary: grazie per il suggerimento! Sembra che sia corretto nel ramo master, potresti provare a costruirlo da solo e poi riprovare ..
seb

68

Questa è ancora una domanda popolare, ma le risposte con il voto più alto non sono aggiornate con la versione corrente del gioco. Ecco un esempio REST funzionante con play 2.2.1:

conf / percorsi:

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
}

Vorrei anche vedere una versione aggiornata della risposta di seb, ma sfortunatamente la tua risposta ha rimosso tutta la magia .xml e .html. :-(
flaschenpost

26

Usa Play! per fare tutto. Scrivere servizi REST in Play è molto semplice.

In primo luogo, il file delle rotte rende semplice scrivere rotte conformi all'approccio REST.

Quindi, scrivi le tue azioni, nel controller, per ogni metodo API che desideri creare.

A seconda di come desideri restituire il risultato (XML, JSON ecc.), Ci sono alcuni metodi che puoi usare. Ad esempio, l'utilizzo del metodo renderJSON consente di visualizzare i risultati molto facilmente. Se vuoi rendere XML, puoi farlo nello stesso modo in cui creeresti un documento HTML nella tua vista.

Ecco un chiaro esempio.

file delle rotte

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

File dell'applicazione

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

getUser.xml file

<user>
   <name>${user.name}</name>
   <dob>${user.dob}</dob>
   .... etc etc
</user>

È possibile scegliere il metodo getUser corretto in base all'intestazione Accept?
Timo Westkämper

è, ma non del tutto affidabile. Se play sa che l'intestazione è una richiesta JSON, proverà a eseguire il rendering di un file getuser.json. Se l'intestazione è un xml, proverà getuser.xml. Tuttavia, è molto più facile da capire, e più simile a REST, per user / User / {id} / type
Codemwnci

29
Non penso che sia più simile a REST specificare il tipo di rappresentazione in modo esplicito nell'URI. È preferibile utilizzare direttamente l'intestazione Accept e non modificare l'URI poiché la risorsa che si desidera visualizzare rimane la stessa. L'esempio sopra potrebbe essere riscritto per avere un solo metodo getUser (Long id) che fa esattamente lo stesso della sua implementazione corrente ma invece di definire un getUserJSON, getUserXML, ecc., Piuttosto definire un modello getUser.json e getUser.xml. Anche se lo rinominerei in user.json / user.xml
seb

Grazie, è molto utile. Apprezzalo!
Gary

1
@seb - puoi espandere il tuo commento in una risposta? Mi piacerebbe vedere un esempio della tecnica che descrivi
Brad Mace

5

L'integrazione con un'implementazione JAX-RS è un possibile approccio alternativo all'utilizzo del routing HTTP integrato di Play. Per un esempio RESTEasy, vedere RESTEasy Play! modulo .

Questo approccio ha senso se hai già investito in JAX-RS o se hai bisogno di alcune delle funzionalità avanzate REST fornite da JAX-RS come la negoziazione dei contenuti. In caso contrario, sarebbe più semplice utilizzare Play direttamente per servire JSON o XML in risposta alle richieste HTTP.



Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.