RESTful on Play! Rahmen

117

Wir planen ein Projekt, das hauptsächlich Inhalte für mobile Apps bereitstellt, benötigen jedoch eine Website.

Meine Frage ist, ob es sinnvoll ist, Jersey oder Restlet zu verwenden, um REST-APIs für unsere mobilen Apps zu entwickeln und dann Play! die Website zu dienen.

Oder ist es sinnvoller, nur Play zu verwenden? alles machen? Wenn ja, wie mache ich REST mit Play! Rahmen?

Gary
quelle

Antworten:

112

Auf Anfrage ein einfacher REST-ähnlicher Ansatz. Es funktioniert fast genauso wie die Lösung von Codemwncis, verwendet jedoch den Accept-Header für die Inhaltsverhandlung. Zuerst die Routendatei:

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

Sie geben hier keinen Inhaltstyp an. Dies ist meiner Meinung nach nur erforderlich, wenn Sie "spezielle" URIs für bestimmte Ressourcen haben möchten. Als würde man eine Route deklarieren, /users/feed/um immer in Atom / RSS zurückzukehren.

Der Anwendungscontroller sieht folgendermaßen aus:

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

Wie Sie sehen, habe ich nur die Methode getUserJSON entfernt und die Methode getUser umbenannt. Damit verschiedene Inhaltstypen funktionieren, müssen Sie jetzt mehrere Vorlagen erstellen. Eine für jeden gewünschten Inhaltstyp. Beispielsweise:

user.xml:

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

user.json:

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

user.html:

<html>...</html>

Dieser Ansatz gibt Browsern immer die HTML-Ansicht, da alle Browser einen Text- / HTML-Inhaltstyp in ihrem Accept-Header senden. Alle anderen Clients (möglicherweise einige JavaScript-basierte AJAX-Anforderungen) können ihren eigenen gewünschten Inhaltstyp definieren. Mit der Methode jQuerys ajax () können Sie Folgendes tun:

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

Damit sollten Sie die Details zu Benutzer mit der ID 1 im JSON-Format erhalten. Play unterstützt derzeit HTML, JSON und XML nativ. Sie können jedoch problemlos einen anderen Typ verwenden, indem Sie entweder der offiziellen Dokumentation folgen oder das Modul für die Inhaltsverhandlung verwenden .

Wenn Sie Eclipse für die Entwicklung verwenden, empfehle ich die Verwendung des REST-Client-Plugins, mit dem Sie Ihre Routen und den entsprechenden Inhaltstyp testen können.

seb
quelle
2
Vielen Dank für die Veröffentlichung. Das Spiel! Dokumente sind einige der besten, die ich gesehen habe, um die Grundstruktur der Dinge zu erklären, aber gelegentlich fehlen detaillierte Beispiele. Wenn die beiden Ansätze am selben Beispiel demonstriert werden, werden die Dinge wirklich klarer.
Brad Mace
Ich probiere Ihr Beispiel aus und bin gespannt, wo die veröffentlichten JSON-Daten in die Benutzerklasse konvertiert werden. In der Funktion createUser finde ich beispielsweise, dass newUser null ist.
Gary
2
@ Gary: Vielleicht hast du "user" anstelle von "newUser" verwendet? Der Name des Controllers und der Formularparameter müssen übereinstimmen. Ich habe ein einfaches Projekt erstellt, das die obige Methode zeigt, einschließlich HTML / XML / JSON-Ausgabe für alle Benutzer unter github.com/sebhoss/play-user-sample
15.
Vielen Dank, ich habe es mit Curl getestet, um JSON-Zeichenfolgen zu senden, und es scheint, dass das Play Framework den Inhaltstyp application / json nicht erkannt hat: groups.google.com/group/play-framework/browse_thread/thread/…
Gary,
@ Gary: Danke für den Hinweis! Es scheint, dass es in der Hauptniederlassung behoben ist. Sie können versuchen, es selbst zu
erstellen
68

Dies ist immer noch eine beliebte Frage, aber die Antworten mit den höchsten Stimmen sind mit der aktuellen Version des Spiels nicht auf dem neuesten Stand. Hier ist ein funktionierendes REST-Beispiel mit Play 2.2.1:

conf / route:

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 / controller / 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
quelle
Ich würde auch gerne eine aktualisierte Version von sebs Antwort sehen, aber leider hat Ihre Antwort alle .xml- und .html-Magie entfernt. :-(
flaschenpost
26

Verwenden Sie Play! alles zu tun. Das Schreiben von REST-Diensten in Play ist sehr, sehr einfach.

Erstens macht es die Routendatei einfach, Routen zu schreiben, die dem REST-Ansatz entsprechen.

Anschließend schreiben Sie Ihre Aktionen für jede API-Methode, die Sie erstellen möchten, in den Controller.

Abhängig davon, wie Sie das Ergebnis zurückgeben möchten (XML, JSON usw.), können Sie einige Methoden verwenden. Mit der renderJSON-Methode können die Ergebnisse beispielsweise sehr einfach gerendert werden. Wenn Sie XML rendern möchten, können Sie dies genauso tun, wie Sie ein HTML-Dokument in Ihrer Ansicht erstellen würden.

Hier ist ein gutes Beispiel.

Routendatei

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

Anwendungsdatei

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 Datei

<user>
   <name>${user.name}</name>
   <dob>${user.dob}</dob>
   .... etc etc
</user>
Codemwnci
quelle
Ist es möglich, die richtige getUser-Methode basierend auf dem Accept-Header auszuwählen?
Timo Westkämper
es ist, aber nicht ganz zuverlässig. Wenn play weiß, dass der Header eine JSON-Anforderung ist, wird versucht, eine getuser.json-Datei zu rendern. Wenn der Header eine XML-Datei ist, wird getuser.xml ausprobiert. Es ist jedoch viel einfacher zu verstehen und REST ähnlicher für Benutzer / Benutzer / {id} / Typ
Codemwnci
29
Ich denke nicht, dass es REST-ähnlicher ist, den Darstellungstyp explizit in der URI anzugeben. Es ist besser, den Accept-Header direkt zu verwenden und den URI nicht zu ändern, da die Ressource, die Sie sehen möchten, dieselbe bleibt. Das obige Beispiel könnte so umgeschrieben werden, dass es nur eine einzige Methode getUser (Long id) enthält, die genau das Gleiche wie die aktuelle Implementierung tut. Statt jedoch getUserJSON, getUserXML usw. zu definieren, definieren Sie lieber eine Vorlage getUser.json und getUser.xml. Obwohl ich das auch in user.json / user.xml umbenennen würde
8.
Danke, das ist sehr hilfreich. Bin dankbar!
Gary
1
@seb - kannst du deinen Kommentar zu einer Antwort erweitern? Ich würde gerne ein Beispiel für die Technik sehen, die Sie beschreiben
Brad Mace
5

Die Integration in eine JAX-RS-Implementierung ist ein möglicher alternativer Ansatz zur Verwendung des integrierten HTTP-Routings von Play. Ein RESTEasy-Beispiel finden Sie im RESTEasy Play! Modul .

Dieser Ansatz ist sinnvoll, wenn Sie bereits in JAX-RS investiert sind oder wenn Sie einige der erweiterten Funktionen von REST benötigen, die JAX-RS bietet, z. B. die Aushandlung von Inhalten. Wenn nicht, wäre es einfacher, Play direkt zu verwenden, um JSON oder XML als Antwort auf HTTP-Anforderungen bereitzustellen.

Peter Hilton
quelle
2

Es scheint, dass dieser Ansatz in Play Version 1.2.3 gebrochen ist. Wenn Sie die von @seb erstellte und zuvor erwähnte Quelle https://github.com/sebhoss/play-user-sample herunterladen , ist die Erstellung eines neuen Benutzerobjekts mithilfe von POST mit einem JSON-Objekt nicht mehr möglich.

Sie müssen bestimmte Methoden für die Erstellung mit json- und xml-POSTs durchführen lassen. Hier beschrieben: https://groups.google.com/forum/#!topic/play-framework/huwtC3YZDlU

tchristensen
quelle