Mengapa bertujuan untuk desain yang tenang?
Prinsip RESTful membawa fitur yang membuat situs web mudah (bagi pengguna manusia acak untuk "berselancar") ke desain API layanan web , sehingga mudah digunakan oleh programmer. REST tidak baik karena REST, itu bagus karena itu bagus. Dan itu bagus terutama karena itu sederhana .
Kesederhanaan HTTP biasa (tanpa amplop SOAP dan POST
layanan kelebihan-URI tunggal ), yang oleh sebagian orang disebut "kekurangan fitur" , sebenarnya merupakan kekuatan terbesarnya . Langsung dari kesalahan, HTTP meminta Anda untuk memiliki addressability dan statelessness : dua keputusan desain dasar yang menjaga HTTP scalable hingga situs mega saat ini (dan layanan mega).
Tapi REST bukan bulletet perak: Kadang-kadang gaya RPC ("Remote Procedure Call" - seperti SOAP) mungkin sesuai , dan kadang-kadang kebutuhan lain didahulukan dari kebaikan Web. Ini baik Yang tidak kita sukai adalah kompleksitas yang tidak perlu . Terlalu sering programmer atau perusahaan membawa Layanan bergaya RPC untuk pekerjaan yang dapat ditangani oleh HTTP tua biasa. Efeknya adalah HTTP direduksi menjadi protokol transport untuk muatan XML yang sangat besar yang menjelaskan apa yang sebenarnya terjadi (bukan metode URI atau HTTP yang memberikan petunjuk tentang hal itu). Layanan yang dihasilkan terlalu rumit, tidak mungkin untuk di-debug, dan tidak akan berfungsi kecuali klien Anda memiliki pengaturan persis seperti yang diinginkan pengembang.
Cara yang sama kode Java / C # tidak bisa berorientasi objek, hanya menggunakan HTTP tidak membuat desain TENANG. Seseorang mungkin terjebak dalam ketergesaan berpikir tentang layanan mereka dalam hal tindakan dan metode jarak jauh yang harus dipanggil. Tidak heran ini sebagian besar akan berakhir dalam layanan RPC-Style (atau REST-RPC-hybrid). Langkah pertama adalah berpikir secara berbeda. Desain yang tenang dapat dicapai dengan banyak cara, salah satunya adalah dengan memikirkan aplikasi Anda dalam hal sumber daya, bukan tindakan:
💡 Alih-alih berpikir dalam hal tindakan yang dapat dilakukan ("lakukan pencarian tempat di peta") ...
... coba pikirkan dalam hal hasil dari tindakan tersebut ("daftar tempat di peta yang cocok dengan kriteria pencarian").
Saya akan ambil contoh di bawah ini. (Aspek kunci lain dari REST adalah penggunaan HATEOAS - Saya tidak menyikatnya di sini, tapi saya membicarakannya dengan cepat di pos lain .)
Masalah desain pertama
Mari kita lihat desain yang diusulkan:
ACTION http://api.animals.com/v1/dogs/1/
Pertama, kita seharusnya tidak mempertimbangkan membuat kata kerja HTTP baru ( ACTION
). Secara umum, ini tidak diinginkan karena beberapa alasan:
- (1) Hanya diberikan layanan URI, bagaimana programmer "acak" tahu
ACTION
kata kerjanya?
- (2) jika programmer tahu itu ada, bagaimana dia tahu semantiknya? Apa arti kata kerja itu?
- (3) properti apa (keamanan, idempoten) yang harus dimiliki seseorang yang memiliki kata kerja itu?
- (4) bagaimana jika programmer memiliki klien yang sangat sederhana yang hanya menangani kata kerja HTTP standar?
- (5) ...
Sekarang mari kita pertimbangkanPOST
untuk menggunakan (saya akan membahas mengapa di bawah ini, ambil saja kata-kata saya sekarang):
POST /v1/dogs/1/ HTTP/1.1
Host: api.animals.com
{"action":"bark"}
Ini mungkin OK ... tetapi hanya jika :
{"action":"bark"}
adalah sebuah dokumen; dan
/v1/dogs/1/
adalah "pengolah dokumen" (seperti pabrik) URI. "Pemroses dokumen" adalah URI yang Anda "singkirkan" dan "lupakan" - prosesor dapat mengarahkan Anda ke sumber yang baru dibuat setelah "melempar". Misalnya URI untuk memposting pesan di layanan perantara pesan, yang, setelah posting akan mengarahkan Anda ke URI yang menunjukkan status pemrosesan pesan.
Saya tidak tahu banyak tentang sistem Anda, tetapi saya sudah bertaruh keduanya tidak benar:
{"action":"bark"}
bukan dokumen , itu sebenarnya metode yang Anda coba untuk menyelinap masuk ke layanan; dan
- yang
/v1/dogs/1/
URI merupakan "anjing" sumber daya (mungkin anjing dengan id==1
) dan tidak prosesor dokumen.
Jadi yang kita tahu sekarang adalah bahwa desain di atas tidak begitu tenang, tapi apa sebenarnya itu? Apa yang sangat buruk tentang itu? Pada dasarnya, itu buruk karena itu adalah URI yang kompleks dengan makna yang kompleks. Anda tidak dapat menyimpulkan apa pun darinya. Bagaimana seorang programmer mengetahui seekor anjing memiliki bark
aksi yang dapat secara diam-diam dimasukkan POST
ke dalamnya?
Merancang panggilan API pertanyaan Anda
Jadi mari kita memotong ke pengejaran dan mencoba untuk merancang gonggongan itu dengan tenang dengan memikirkan sumber daya . Izinkan saya mengutip buku Restful Web Services :
Sebuah POST
permintaan merupakan upaya untuk menciptakan sumber daya baru dari yang sudah ada. Sumber daya yang ada dapat menjadi induk dari yang baru dalam arti struktur data, cara akar pohon adalah induk dari semua simpul daunnya. Atau sumber daya yang ada dapat berupa sumber daya "pabrik" khusus
yang tujuannya hanya untuk menghasilkan sumber daya lain. Representasi yang dikirim bersama dengan POST
permintaan menggambarkan keadaan awal sumber daya baru. Seperti PUT, POST
permintaan tidak perlu menyertakan representasi sama sekali.
Dengan mengikuti uraian di atas, kita dapat melihat bahwa bark
dapat dimodelkan sebagai sub-sumber daya daridog
(karena a bark
terkandung dalam seekor anjing, yaitu, kulit kayu "kulit kayu" oleh seekor anjing).
Dari alasan itu kita sudah mendapat:
- Metodenya adalah
POST
- Sumber dayanya adalah
/barks
, sub- sumber daya anjing:, /v1/dogs/1/barks
mewakili bark
"pabrik". URI itu unik untuk setiap anjing (karena di bawah /v1/dogs/{id}
).
Sekarang setiap kasus daftar Anda memiliki perilaku tertentu.
1. kulit hanya mengirim email ke dog.email
dan tidak merekam apa pun.
Pertama, apakah menggonggong (mengirim email) tugas yang sinkron atau tidak sinkron? Kedua, apakah bark
permintaan memerlukan dokumen apa pun (email, mungkin) atau kosong?
1.1 kulit mengirim email ke dog.email
dan tidak merekam apa-apa (sebagai tugas sinkron)
Kasus ini sederhana. Panggilan ke barks
sumber daya pabrik menghasilkan kulit kayu (e-mail terkirim) segera dan responsnya (jika OK atau tidak) segera diberikan:
POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=
(entity-body is empty - or, if you require a **document**, place it here)
200 OK
Saat merekam (perubahan) tidak ada, 200 OK
sudah cukup. Ini menunjukkan bahwa semuanya berjalan seperti yang diharapkan.
1.2 kulit mengirim email ke dog.email
dan tidak merekam apa-apa (sebagai tugas tidak sinkron)
Dalam hal ini, klien harus memiliki cara untuk melacak bark
tugas. The bark
tugas kemudian harus menjadi sumber daya dengan itu sendiri URI .:
POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=
{document body, if needed;
NOTE: when possible, the response SHOULD contain a short hypertext note with a hyperlink
to the newly created resource (bark) URI, the same returned in the Location header
(also notice that, for the 202 status code, the Location header meaning is not
standardized, thus the importance of a hipertext/hyperlink response)}
202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44
Dengan cara ini, masing bark
- masing dapat dilacak. Klien kemudian dapat mengeluarkan a GET
ke bark
URI untuk mengetahui keadaan saat ini. Mungkin bahkan menggunakan DELETE
untuk membatalkannya.
2. kulit kayu mengirim email ke dog.email
dan kemudian bertambah dog.barkCount
1
Yang ini bisa lebih sulit, jika Anda ingin memberi tahu klien bahwa dog
sumber daya diubah:
POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=
{document body, if needed; when possible, containing a hipertext/hyperlink with the address
in the Location header -- says the standard}
303 See Other
Location: http://api.animals.com/v1/dogs/1
Dalam hal ini, location
maksud header adalah memberi tahu klien bahwa ia harus melihatnya dog
. Dari HTTP RFC tentang303
:
Metode ini ada terutama untuk memungkinkan output dari
POST
skrip-aktif untuk mengarahkan agen pengguna ke sumber daya yang dipilih.
Jika tugas tidak sinkron, bark
subresource diperlukan seperti 1.2
situasinya dan 303
harus dikembalikan pada GET .../barks/Y
saat tugas selesai.
3. kulit membuat catatan " bark
" baru dengan bark.timestamp
merekam ketika kulit terjadi. Ini juga bertambah dog.barkCount
1.
POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=
(document body, if needed)
201 Created
Location: http://api.animals.com/v1/dogs/1/barks/a65h44
Di sini, bark
itu dibuat karena permintaan, jadi statusnya 201 Created
diterapkan.
Jika pembuatannya tidak sinkron, maka 202 Accepted
diperlukan ( seperti yang dikatakan HTTP RFC ).
Stempel waktu yang disimpan adalah bagian dari bark
sumber daya dan dapat diambil dengan GET
itu. Anjing yang diperbarui dapat "didokumentasikan" di GET dogs/X/barks/Y
dalamnya juga.
4. kulit kayu menjalankan perintah sistem untuk menarik kode anjing versi terbaru dari Github. Kemudian mengirim pesan teks untuk dog.owner
memberi tahu mereka bahwa kode anjing baru dalam produksi.
Kata-kata yang satu ini rumit, tetapi cukup banyak tugas asinkron yang sederhana:
POST /v1/dogs/1/barks HTTP/1.1
Host: api.animals.com
Authorization: Basic mAUhhuE08u724bh249a2xaP=
(document body, if needed)
202 Accepted
Location: http://api.animals.com/v1/dogs/1/barks/a65h44
Klien kemudian akan mengeluarkan GET
s untuk /v1/dogs/1/barks/a65h44
mengetahui keadaan saat ini (jika kode itu ditarik, itu e-mail dikirim ke pemilik dan semacamnya). Setiap kali anjing berubah, a 303
dapat diterapkan.
Membungkus
Mengutip Roy Fielding :
Satu-satunya hal yang dituntut oleh REST dari metode adalah bahwa mereka didefinisikan secara seragam untuk semua sumber daya (yaitu, sehingga perantara tidak harus mengetahui jenis sumber daya untuk memahami makna permintaan).
Dalam contoh di atas, POST
dirancang secara seragam. Itu akan membuat anjing " bark
". Itu tidak aman (artinya kulit kayu memiliki efek pada sumber daya), atau idempoten (setiap permintaan menghasilkan yang baru bark
), yang cocok dengan POST
kata kerja dengan baik.
Seorang programmer akan tahu: a POST
to barks
yields a bark
. Kode status respons (juga dengan entitas-badan dan header bila perlu) melakukan pekerjaan menjelaskan apa yang berubah dan bagaimana klien dapat dan harus melanjutkan.
Catatan: Sumber utama yang digunakan adalah: " Restful Web Services " buku, HTTP RFC dan blog Roy Fielding .
Edit:
Pertanyaan dan dengan demikian jawabannya telah sedikit berubah sejak pertama kali dibuat. Pertanyaan awal ditanyakan tentang desain URI seperti:
ACTION http://api.animals.com/v1/dogs/1/?action=bark
Di bawah ini adalah penjelasan mengapa itu bukan pilihan yang baik:
Bagaimana klien memberi tahu server APA YANG HARUS DILAKUKAN dengan data adalah informasi metode .
- Layanan web yang tenang menyampaikan informasi metode dalam metode HTTP.
- Layanan RPC-Style dan SOAP yang khas menyimpannya di entitas-badan dan header HTTP.
BAGIAN yang dari data [klien ingin server] untuk beroperasi adalah informasi pelingkupan .
- Layanan tenang menggunakan URI. Layanan SOAP / RPC-Style sekali lagi menggunakan entitas-tubuh dan header HTTP.
Sebagai contoh, ambil Google URI http://www.google.com/search?q=DOG
. Di sana, informasi metode adalah GET
dan informasi pelingkupannya /search?q=DOG
.
Singkat cerita:
- Dalam arsitektur RESTful , informasi metode masuk ke metode HTTP.
- Dalam Arsitektur Berorientasi Sumber Daya , informasi pelingkupan masuk ke URI.
Dan aturan praktisnya:
Jika metode HTTP tidak cocok dengan informasi metode, layanan ini tidak tenang. Jika informasi pelingkupan tidak dalam URI, layanan tidak berorientasi sumber daya.
Anda dapat meletakkan "kulit" "tindakan" di URL (atau di badan-entitas) dan gunakan POST
. Tidak masalah di sana, ini berfungsi, dan mungkin cara paling sederhana untuk melakukannya, tetapi ini tidak tenang .
Untuk menjaga layanan Anda tetap tenang, Anda mungkin harus mengambil langkah mundur dan memikirkan apa yang benar-benar ingin Anda lakukan di sini (efek apa yang akan terjadi pada sumber daya).
Saya tidak dapat berbicara tentang kebutuhan bisnis spesifik Anda, tetapi izinkan saya memberi Anda sebuah contoh: Pertimbangkan layanan pemesanan yang tenang di mana pesanan berada di URI seperti example.com/order/123
.
Sekarang katakanlah kita ingin membatalkan pesanan, bagaimana kita akan melakukannya? Orang mungkin tergoda untuk berpikir bahwa itu adalah "pembatalan" "tindakan" dan mendesainnya sebagai POST example.com/order/123?do=cancel
.
Itu tidak tenang, seperti yang kita bicarakan di atas. Sebagai gantinya, kami mungkin PUT
representasi baru dari order
dengan canceled
elemen yang dikirim ke true
:
PUT /order/123 HTTP/1.1
Content-Type: application/xml
<order id="123">
<customer id="89987">...</customer>
<canceled>true</canceled>
...
</order>
Dan itu saja. Jika pesanan tidak dapat dibatalkan, kode status tertentu dapat dikembalikan. (Desain sub-sumber daya, seperti POST /order/123/canceled
dengan entitas-badan true
mungkin, untuk kesederhanaan, juga tersedia.)
Dalam skenario spesifik Anda, Anda dapat mencoba sesuatu yang serupa. Dengan begitu, saat anjing menggonggong, misalnya, GET
at /v1/dogs/1/
dapat memasukkan informasi itu (misalnya <barking>true</barking>
) . Atau ... jika itu terlalu rumit, kendurkan persyaratan RESTful Anda dan ikuti POST
.
Memperbarui:
Saya tidak ingin membuat jawaban terlalu besar, tetapi perlu beberapa saat untuk membiasakan mengekspos algoritma ( aksi ) sebagai satu set sumber daya. Alih-alih berpikir dalam hal tindakan ( "melakukan pencarian tempat di peta" ), orang perlu berpikir dalam hal hasil dari tindakan itu ( "daftar tempat di peta yang cocok dengan kriteria pencarian" ).
Anda mungkin menemukan diri Anda kembali ke langkah ini jika Anda menemukan bahwa desain Anda tidak sesuai dengan antarmuka seragam HTTP.
Variabel kueri adalah pelingkupan informasi , tetapi tidak menunjukkan sumber daya baru ( /post?lang=en
jelas sumber daya yang sama dengan /post?lang=jp
, hanya representasi yang berbeda). Sebaliknya, mereka digunakan untuk menyampaikan status klien (seperti ?page=10
, sehingga status tidak disimpan di server; ?lang=en
juga merupakan contoh di sini) atau memasukkan parameter ke sumber daya algoritmik ( /search?q=dogs
, /dogs?code=1
). Sekali lagi, bukan sumber daya yang berbeda.
Properti (metode) kata kerja HTTP:
Poin jelas lainnya yang ditampilkan ?action=something
dalam URI adalah tidak TETAP, adalah properti dari kata kerja HTTP:
GET
dan HEAD
aman (dan idempoten);
PUT
dan DELETE
hanya idempoten;
POST
bukan keduanya.
Keamanan : Permintaan GET
atau HEAD
permintaan adalah permintaan untuk membaca beberapa data, bukan permintaan untuk mengubah status server apa pun. Klien dapat membuat GET
atau HEAD
meminta 10 kali dan itu sama dengan membuatnya sekali, atau tidak pernah membuatnya sama sekali .
Idempoten : Operasi idempoten di salah satu yang memiliki efek yang sama apakah Anda menerapkannya sekali atau lebih dari sekali (dalam matematika, mengalikan dengan nol adalah idempoten). Jika Anda DELETE
sumber daya sekali, menghapus lagi akan memiliki efek yang sama (sumber daya GONE
sudah ada).
POST
tidak aman atau idempoten. Membuat dua POST
permintaan yang identik dengan sumber daya 'pabrik' mungkin akan menghasilkan dua sumber daya bawahan yang mengandung informasi yang sama. Dengan kelebihan beban (metode dalam URI atau entitas-tubuh) POST
, semua taruhan dimatikan.
Kedua properti ini penting untuk keberhasilan protokol HTTP (melalui jaringan yang tidak dapat diandalkan!): Berapa kali Anda memperbarui ( GET
) halaman tanpa menunggu sampai sepenuhnya dimuat?
Membuat tindakan dan menempatkannya di URL dengan jelas melanggar kontrak metode HTTP. Sekali lagi, teknologi memungkinkan Anda, Anda bisa melakukannya, tetapi itu bukan desain yang tenang.