Tenang di Play! kerangka

117

Kami merencanakan sebuah proyek yang utamanya menyajikan konten ke aplikasi seluler, tetapi perlu memiliki situs web.

Pertanyaan saya adalah apakah masuk akal menggunakan Jersey atau Restlet untuk mengembangkan REST API untuk aplikasi seluler kita, dan kemudian menggunakan Play! untuk melayani situs web.

Atau lebih masuk akal jika hanya menggunakan Play! melakukan semuanya? Jika demikian, bagaimana melakukan REST dengan Play! kerangka?

Gary
sumber

Jawaban:

112

Sesuai permintaan, pendekatan seperti REST sederhana. Ini bekerja hampir sama dengan cara kerja solusi Codemwncis tetapi menggunakan header Terima untuk negosiasi konten. Pertama file rute:

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

Anda tidak menentukan tipe konten apa pun di sini. IMHO hanya diperlukan jika Anda ingin memiliki URI "khusus" untuk sumber daya tertentu. Seperti mendeklarasikan rute /users/feed/untuk selalu kembali di Atom / RSS.

Pengontrol Aplikasi terlihat seperti ini:

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

Seperti yang Anda lihat, saya hanya menghapus metode getUserJSON dan mengganti nama metode getUser. Untuk tipe konten yang berbeda untuk bekerja, Anda sekarang harus membuat beberapa templat. Satu untuk setiap jenis konten yang diinginkan. Sebagai contoh:

user.xml:

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

user.json:

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

user.html:

<html>...</html>

Pendekatan ini memberikan browser selalu tampilan HTML, karena semua browser mengirim jenis konten teks / html di header Terima mereka. Semua klien lain (mungkin beberapa permintaan AJAX berbasis JavaScript) dapat menentukan jenis konten yang mereka inginkan. Menggunakan metode jQuerys ajax () Anda dapat melakukan hal berikut:

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

Yang akan memberi Anda detail tentang Pengguna dengan ID 1 dalam format JSON. Play saat ini mendukung HTML, JSON dan XML secara native, tetapi Anda dapat dengan mudah menggunakan jenis yang berbeda dengan mengikuti dokumentasi resmi atau menggunakan modul negosiasi konten .

Jika Anda menggunakan Eclipse untuk pengembangan, saya sarankan menggunakan plugin klien REST yang memungkinkan Anda menguji rute dan jenis konten yang sesuai.

seb
sumber
2
Terima kasih telah memposting ini. The Play! dokumen adalah beberapa yang terbaik yang pernah saya lihat untuk menjelaskan struktur dasar berbagai hal, tetapi terkadang kurang dalam contoh rinci. Memiliki dua pendekatan yang ditunjukkan pada contoh yang sama benar-benar memperjelas semuanya.
Brad Mace
Saya mencoba contoh Anda, saya ingin tahu di mana data JSON yang diposting dikonversi ke kelas Pengguna. misalnya, di dalam fungsi createUser saya menemukan newUser adalah null.
Gary
2
@Gary: Mungkin Anda menggunakan "user" daripada "newUser"? Nama pengontrol dan parameter formulir harus cocok. Saya telah membuat proyek sederhana yang menunjukkan metode di atas, termasuk output HTML / XML / JSON untuk semua pengguna di github.com/sebhoss/play-user-sample
seb
Terima kasih, saya mengujinya dengan menggunakan curl untuk mengirim string JSON, dan tampaknya kerangka kerja bermain tidak mengenali jenis konten aplikasi / json: groups.google.com/group/play-framework/browse_thread/thread/…
Gary
@ Gary: Terima kasih atas petunjuknya! Tampaknya sudah diperbaiki di cabang master, Anda dapat mencoba membangunnya sendiri dan kemudian mengujinya lagi ..
seb
68

Ini masih pertanyaan populer, tetapi jawaban yang dipilih terbanyak tidak mutakhir dengan versi permainan saat ini. Berikut adalah contoh REST yang berfungsi dengan 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
sumber
Saya juga ingin melihat versi terbaru dari Jawaban seb, tetapi sayangnya jawaban Anda menghapus semua keajaiban .xml dan .html. :-(
flaschenpost
26

Gunakan Play! untuk melakukan semuanya. Menulis layanan REST di Play sangat mudah.

Pertama, file routes membuatnya mudah untuk menulis rute yang sesuai dengan pendekatan REST.

Kemudian, Anda menulis tindakan Anda, di pengontrol, untuk setiap metode API yang ingin Anda buat.

Bergantung pada bagaimana Anda ingin mengembalikan hasilnya (XML, JSON dll), ada beberapa metode yang dapat Anda gunakan. Misalnya, menggunakan metode renderJSON, memungkinkan hasil dirender dengan sangat mudah. Jika Anda ingin merender XML, Anda dapat melakukannya dengan cara yang sama seperti Anda membuat dokumen HTML di View Anda.

Ini adalah contoh yang bagus.

rute file

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 aplikasi

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>
Codemwnci
sumber
Apakah mungkin untuk memilih metode getUser yang tepat berdasarkan header Terima?
Timo Westkämper
memang, tetapi tidak sepenuhnya dapat diandalkan. Jika play mengetahui bahwa header adalah permintaan JSON, maka play akan mencoba merender file getuser.json. Jika header adalah xml, maka getuser.xml akan dicoba. Namun, jauh lebih mudah untuk dipahami, dan lebih seperti REST, untuk pengguna / Pengguna / {id} / type
Codemwnci
29
Saya tidak berpikir itu lebih seperti REST untuk menentukan tipe representasi secara eksplisit di URI. Lebih baik menggunakan header Terima secara langsung dan jangan ubah URI karena sumber daya yang ingin Anda lihat tetap sama. Contoh di atas dapat ditulis ulang menjadi hanya satu metode getUser (Long id) yang berfungsi persis sama dengan implementasi saat ini, tetapi alih-alih menentukan getUserJSON, getUserXML, dll. Anda lebih suka mendefinisikan template getUser.json dan getUser.xml. Meskipun saya akan mengganti namanya menjadi user.json / user.xml juga
seb
Terima kasih, ini sangat membantu. Menghargai itu!
Gary
1
@seb - dapatkah Anda memperluas komentar Anda menjadi sebuah jawaban? Saya ingin melihat contoh teknik yang Anda gambarkan
Brad Mace
5

Mengintegrasikan dengan implementasi JAX-RS adalah pendekatan alternatif yang mungkin untuk menggunakan perutean HTTP bawaan Play. Untuk contoh RESTEasy, lihat RESTEasy Play! modul .

Pendekatan ini masuk akal jika Anda sudah berinvestasi di JAX-RS, atau jika Anda memerlukan beberapa fitur lanjutan REST yang disediakan JAX-RS seperti negosiasi konten. Jika tidak, akan lebih sederhana jika hanya menggunakan Play secara langsung untuk menyajikan JSON atau XML sebagai respons terhadap permintaan HTTP.

Peter Hilton
sumber
2

Sepertinya pendekatan ini rusak di Play versi 1.2.3. Jika Anda mendownload sumber yang dilakukan oleh @seb dan disebutkan sebelumnya https://github.com/sebhoss/play-user-sample , pembuatan objek pengguna baru menggunakan POST dengan objek JSON tidak lagi memungkinkan.

Anda perlu memiliki metode khusus untuk pembuatan yang dilakukan dengan menggunakan json dan xml POST. Diuraikan di sini: https://groups.google.com/forum/#!topic/play-framework/huwtC3YZDlU

tchristensen
sumber