API TENANG. Haruskah saya mengembalikan objek yang telah dibuat / diperbarui?

36

Saya merancang layanan web RESTful menggunakan WebApi dan bertanya-tanya apa tanggapan HTTP dan badan respons untuk kembali saat memperbarui / membuat objek.

Sebagai contoh saya dapat menggunakan metode POST untuk mengirim beberapa JSON ke layanan web dan kemudian membuat objek. Apakah ini praktik terbaik untuk kemudian mengatur status HTTP ke diciptakan (201) atau ok (200) dan hanya mengembalikan pesan seperti "Karyawan Baru ditambahkan", atau mengembalikan objek yang dikirim awalnya?

Hal yang sama berlaku untuk metode PUT. Status HTTP mana yang paling baik digunakan dan apakah saya harus mengembalikan objek yang telah dibuat atau hanya pesan? Mengingat fakta bahwa pengguna tahu objek apa yang mereka coba buat / perbarui.

Adakah pikiran?

Contoh:

Tambahkan Karyawan Baru:

POST /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Name" : "Joe Bloggs",
        "Department" : "Finance"
    }
}

Perbarui karyawan yang ada:

PUT /api/employee HTTP/1.1
Host: localhost:8000
Content-Type: application/json

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

Tanggapan:

Respons dengan objek dibuat / diperbarui

HTTP/1.1 201 Created
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Employee": {
        "Id" : 1
        "Name" : "Joe Bloggs",
        "Department" : "IT"
    }
}

Respons dengan pesan adil:

HTTP/1.1 200 OK
Content-Length: 39
Content-Type: application/json; charset=utf-8
Date: Mon, 28 Mar 2016 14:32:39 GMT

{
    "Message": "Employee updated"
}

Respons dengan kode status yang adil:

HTTP/1.1 204 No Content
Content-Length: 39
Date: Mon, 28 Mar 2016 14:32:39 GMT
iswinky
sumber
2
Pertanyaan yang bagus, tetapi menggunakan istilah "praktik terbaik" adalah hal yang tabu di situs ini meta.programmers.stackexchange.com/questions/2442/… Anda mungkin hanya ingin menuliskan kembali pertanyaan itu. meta.programmers.stackexchange.com/questions/6967/…
Snoop
3
Sebagai tindak lanjut, apakah ide yang baik untuk memiliki bendera dalam permintaan sehingga (misalnya) aplikasi seluler dapat mengembalikan seluruh objek saat menggunakan WiFi, tetapi hanya ID saat menggunakan data seluler? Apakah ada tajuk yang harus digunakan untuk itu agar tidak mencemari JSON?
Andrew mengatakan Reinstate Monica
@AndrewPiliser Ide yang menarik, meskipun saya pribadi berpikir yang terbaik adalah memilih satu pendekatan dan berpegang teguh pada itu. Kemudian ketika aplikasi Anda berkembang atau menjadi lebih populer, optimalkan
iswinky
@AndrewPiliser ide Anda sangat mirip dengan UPDATE/INSERT ... RETURNINGvarian Postgresql untuk SQL. Ini sangat berguna, terutama karena menyimpan pengajuan data baru dan meminta atomic versi terbaru.
beldaz

Jawaban:

31

Seperti kebanyakan hal, itu tergantung. Pengorbanan Anda adalah kemudahan penggunaan versus ukuran jaringan. Dapat sangat membantu bagi klien untuk melihat sumber daya yang dibuat. Ini mungkin termasuk bidang yang diisi oleh server, seperti waktu pembuatan terakhir. Karena Anda tampaknya memasukkan idalih - alih menggunakan hateoas, klien mungkin ingin melihat id untuk sumber daya yang baru saja mereka POSTedit.

Jika Anda tidak menyertakan sumber daya yang dibuat, tolong jangan membuat pesan sewenang-wenang. Bidang 2xx dan Lokasi adalah informasi yang cukup bagi klien untuk yakin bahwa permintaan mereka ditangani dengan benar.

Eric Stein
sumber
+1 Tujuan hateoas untuk tidak membiarkan klien menulis uri juga dapat dicapai dengan mengizinkan klien mengisi templat URL yang disediakan server dengan ID tertentu. Ya, klien "menyusun" tetapi hanya dengan cara "isi yang kosong". Meskipun tidak murni HATEOAS, ia mencapai tujuan dan membuat bekerja dengan objek yang memiliki jumlah "aksi" yang banyak (uri) sedikit lebih sensitif terhadap bandwidth, belum lagi ketika Anda meletakkan objek-objek itu dalam daftar (besar).
Marjan Venema
3
Memberi +1 pada saran "tolong jangan membuat pesan sembarang"
HairOfTheDog
Apakah "no arbitrary message" fokus pada pesan string atau nilai pengembalian apa pun yang bukan sumber daya yang dibuat? Saya fokus pada kasus di mana kami mengembalikan id dari sumber daya yang dibuat (tetapi bukan sumber daya itu sendiri) dan bertanya-tanya di mana ini cocok.
Flater
12

Secara pribadi, saya selalu kembali saja 200 OK.

Mengutip pertanyaan Anda

Mengingat fakta bahwa pengguna tahu objek apa yang mereka coba buat / perbarui.

Mengapa menambahkan bandwidth ekstra (yang mungkin harus dibayar) untuk memberi tahu klien apa yang sudah diketahui?

Mawg
sumber
1
Itulah yang saya pikirkan, jika tidak valid Anda dapat mengembalikan pesan validasi, tetapi jika valid dan dibuat / dimutakhirkan maka periksa kode status HTTP dan perlihatkan pesan kepada pengguna, mis. "Hore" berdasarkan itu
iswinky
3
Lihat stackoverflow.com/a/827045/290182 tentang 200/ 204 No Contentuntuk menghindari jQuery yang membingungkan dan sejenisnya.
beldaz
10

@ iswinky Saya akan selalu mengirim kembali payload jika POST dan PUT.

Dalam hal POST, Anda dapat membuat entitas dengan ID internal atau UUID. Oleh karena itu masuk akal untuk mengirim kembali muatan.

Demikian pula dalam kasus PUT, Anda mungkin mengabaikan beberapa bidang pengguna (nilai yang tidak dapat diubah, katakanlah), atau dalam kasus PATCH, data mungkin telah diubah oleh pengguna lain juga.

Mengirim data kembali seperti yang ada selalu merupakan ide yang bagus dan jelas tidak membahayakan. Jika pemanggil tidak perlu untuk data yang dikembalikan ini, maka dia tidak akan memprosesnya tetapi hanya akan mengkonsumsi statusCode. Selain itu, mereka dapat menggunakan data ini sebagai sesuatu untuk memperbarui UI.

Hanya dalam kasus DELETE saya tidak akan mengirim kembali muatan dan akan melakukan 200 dengan konten respons, atau 204 tanpa konten respons.

Sunting: Berkat beberapa komentar dari bawah, saya menulis ulang jawaban saya. Saya masih bertahan dengan cara saya mendesain API saya dan mengirim kembali tanggapan tetapi saya pikir masuk akal untuk memenuhi syarat beberapa pemikiran desain saya.

a) Ketika saya mengatakan kirim kembali muatan, saya sebenarnya bermaksud mengatakan kirim kembali data sumber daya, bukan muatan yang sama yang datang dalam permintaan. Mis: jika Anda mengirim payload buat, saya mungkin di backend membuat entitas lain seperti UUID dan (mungkin) cap waktu dan bahkan beberapa koneksi (grafik). Saya akan mengirimkan semua ini kembali sebagai tanggapan (bukan hanya permintaan muatan apa adanya - yang tidak ada gunanya).

b) Saya tidak akan mengirim kembali tanggapan jika payloadnya sangat besar. Saya sudah membahas ini di komentar, tetapi yang ingin saya sampaikan adalah bahwa saya akan mencoba yang terbaik untuk merancang API atau sumber daya saya sehingga tidak harus memiliki muatan yang sangat besar. Saya akan mencoba memecah sumber daya menjadi entitas yang lebih kecil dan dikelola sehingga setiap sumber daya didefinisikan oleh atribut JSON 15-20 dan tidak lebih besar.

Dalam hal objek sangat besar atau objek induk diperbarui, maka saya akan mengirim kembali struktur bersarang sebagai hrefs.

Intinya adalah saya pasti akan mencoba mengirim kembali data yang masuk akal bagi konsumen / UI untuk segera diproses dan dilakukan dengan tindakan API atom daripada harus pergi dan mengambil 2-5 API lagi hanya untuk memperbarui posting UI. pembuatan / pembaruan data.

API server ke server mungkin berpikir berbeda tentang ini. Saya fokus pada API yang mendorong pengalaman pengguna.

ksprashu
sumber
Saya bisa melihat banyak situasi di mana mengirim kembali seluruh muatan adalah ide yang buruk, ketika muatannya besar.
beldaz
2
@ Beldaz sepenuhnya setuju. YMMV berdasarkan desain API REST. Saya biasanya menghindari objek yang sangat besar dan memecahnya menjadi serangkaian sub-sumber daya / PUT. Jika payloadnya sangat besar ada cara yang lebih baik untuk melakukan ini, dan di dalamnya Anda ingin melakukan HATEOAS (seperti kata Marjan di atas) di mana Anda mengembalikan referensi ke objek daripada objek itu sendiri.
ksprashu
@ksprashu: "Karena itu masuk akal untuk mengirim kembali muatan" - Saya menemukan ini adalah ide yang buruk, karena dengan demikian sumber daya dapat diperoleh dalam banyak cara: melalui GET, sebagai respons dari POST, sebagai respons dari PUT. Ini berarti bahwa klien memperoleh 3 sumber daya dengan struktur yang berpotensi berbeda . Jika Anda hanya mengembalikan URI (lokasi), tanpa badan, maka satu-satunya cara untuk mendapatkan sumber daya adalah GET. Ini akan memastikan bahwa klien selalu mendapatkan tanggapan yang konsisten.
mentallurg
@mentallurg Saya menyadari saya mungkin tidak mengatakan ini dengan benar. Terima kasih telah menunjukkannya. Saya sudah mengedit jawaban saya. Beri tahu saya jika ini lebih masuk akal.
ksprashu
Selama Anda menerapkan layanan untuk pekerjaan rumah Anda, itu tidak terlalu penting. Lakukan sesuka Anda. Hemat waktu Anda.
mentallurg
9

Mengacu pada standar tautan RFC , Anda harus mengembalikan 201 (dibuat) status agar berhasil menyimpan sumber daya permintaan menggunakan Post. Di sebagian besar aplikasi id dari sumber daya dihasilkan oleh server itu sendiri, jadi itu adalah praktik yang baik untuk mengembalikan id dari sumber daya yang dibuat. Mengembalikan seluruh objek adalah overhead untuk permintaan Posting. Praktik ideal adalah mengembalikan lokasi URL sumber daya yang baru dibuat.

Misalnya, Anda bisa merujuk ke contoh berikut yang menyimpan Objek Karyawan ke dalam database dan mengembalikan URL objek sumber daya yang baru dibuat sebagai tanggapan.

@RequestMapping(path = "/employees", method = RequestMethod.POST)
public ResponseEntity<Object> saveEmployee(@RequestBody Employee employee) {
        int id = employeeService.saveEmployee(employee);
        URI uri = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}").buildAndExpand(id).toUri();
        return ResponseEntity.created(uri).build();
}

Endpoint sisanya ini akan mengembalikan respons sebagai:

Status 201 - DICIPTAKAN

Lokasi Header → http: // localhost: 8080 / karyawan / 1

Shubham Bondarde
sumber
Bagus dan bersih - akan mengikuti ini dari sekarang dan seterusnya
Hassan Tareq
0

Saya akan membuat payload di tubuh kembali bersyarat ke parameter HTTP.

Lebih sering daripada tidak itu yang terbaik adalah mengembalikan semacam konten kembali ke konsumen API untuk menghindari pulang pergi yang tidak perlu (salah satu alasan mengapa GraphQL ada.)

Sebenarnya, ketika aplikasi kita menjadi lebih intensif dan terdistribusi, saya coba perhatikan panduan ini:

Pedoman Saya :

Setiap kali ada kasus penggunaan yang menuntut GET segera setelah POST atau PUT, itu adalah kasus di mana mungkin lebih baik mengembalikan sesuatu dalam respons POST / PUT.

Bagaimana ini dilakukan, dan apa jenis konten untuk kembali keluar dari PUT atau POST, itu khusus aplikasi. Sekarang, akan menarik jika aplikasi dapat mengukur tipe "konten" di badan respons (apakah kita hanya menginginkan lokasi objek baru, atau beberapa bidang, atau seluruh objek, dll.)

Sebuah aplikasi dapat menetapkan seperangkat parameter yang POST / PUT dapat terima untuk mengontrol jenis "konten" untuk kembali dalam badan respons. Atau bisa menyandikan semacam permintaan GraphQL sebagai parameter (saya bisa melihat ini berguna tetapi juga menjadi mimpi buruk pemeliharaan.)

Either way, menurut saya bahwa:

  1. Tidak apa-apa (dan kemungkinan besar diinginkan) untuk mengembalikan sesuatu di badan respons POST / PUT.
  2. Cara ini dilakukan adalah spesifik aplikasi dan hampir tidak mungkin untuk digeneralisasi.
  3. Anda tidak ingin mengembalikan "konteks" ukuran besar secara default (kebisingan lalu lintas yang mengalahkan seluruh alasan menjauh dari POST-diikuti-oleh-GET.)

Jadi, 1) melakukannya, tetapi 2) tetap sederhana.

Opsi lain yang saya lihat adalah orang-orang yang membuat titik akhir alternatif (misalnya, / pelanggan untuk POST / PUT yang tidak mengembalikan apa pun di badan dan / customer_with_detail untuk POST / PUT ke / pelanggan, tetapi itu mengembalikan sesuatu di badan tanggapan.)

Saya akan menghindari pendekatan ini. Apa yang terjadi ketika Anda secara sah perlu mengembalikan berbagai jenis konten? Satu titik akhir per jenis konten? Tidak dapat diskalakan atau dipertahankan.

luis.espinal
sumber