Memahami REST: Kata Kerja, kode kesalahan, dan otentikasi

602

Saya mencari cara untuk membungkus API di sekitar fungsi default di aplikasi web berbasis PHP, database dan CMS saya.

Saya telah melihat sekeliling dan menemukan beberapa kerangka kerja "kerangka". Selain jawaban dalam pertanyaan saya, ada Tonic , kerangka kerja REST yang saya suka karena sangat ringan.

Saya suka REST yang terbaik untuk kesederhanaannya, dan ingin membuat arsitektur API berdasarkan itu. Saya mencoba memahami prinsip-prinsip dasar dan belum sepenuhnya memahaminya. Karena itu, sejumlah pertanyaan.

1. Apakah saya memahaminya dengan benar?

Katakanlah saya punya "pengguna" sumber daya. Saya dapat mengatur sejumlah URI seperti:

/api/users     when called with GET, lists users
/api/users     when called with POST, creates user record
/api/users/1   when called with GET, shows user record
               when called with PUT, updates user record
               when called with DELETE, deletes user record

Apakah ini representasi yang benar dari arsitektur RESTful sejauh ini?

2. Saya perlu lebih banyak kata kerja

Buat, Perbarui dan Hapus mungkin cukup dalam teori, tetapi dalam praktiknya saya akan membutuhkan lebih banyak kata kerja. Saya menyadari ini adalah hal-hal yang dapat disematkan dalam permintaan pembaruan, tetapi itu adalah tindakan spesifik yang dapat memiliki kode pengembalian spesifik dan saya tidak ingin membuang semuanya menjadi satu tindakan.

Beberapa yang terlintas dalam contoh pengguna adalah:

activate_login
deactivate_login
change_password
add_credit

bagaimana cara saya mengungkapkan tindakan seperti yang ada di arsitektur URL RESTful?

Insting saya adalah melakukan panggilan GET ke URL

/api/users/1/activate_login 

dan mengharapkan kode status kembali.

Itu menyimpang dari gagasan menggunakan kata kerja HTTP, meskipun. Bagaimana menurut anda?

3. Cara mengembalikan pesan dan kode kesalahan

Sebagian besar keindahan REST berasal dari penggunaan metode HTTP standar. Pada kesalahan, saya memancarkan header dengan kode status kesalahan 3xx, 4xx atau 5xx. Untuk deskripsi kesalahan terperinci, saya dapat menggunakan tubuh (kanan?). Sejauh ini bagus. Tetapi apa yang akan menjadi cara untuk mengirimkan kode kesalahan hak milik yang lebih terperinci dalam menggambarkan apa yang salah (mis. "Gagal terhubung ke basis data", atau "basis data masuk salah")? Jika saya memasukkannya ke dalam tubuh bersama dengan pesan, saya harus menguraikannya setelah itu. Apakah ada header standar untuk hal semacam ini?

4. Cara melakukan otentikasi

  • Seperti apa otentikasi berbasis kunci API yang mengikuti prinsip REST?
  • Adakah poin kuat dari penggunaan sesi saat mengautentikasi klien REST, selain itu itu merupakan pelanggaran terang-terangan terhadap prinsip REST? :) (hanya setengah bercanda di sini, otentikasi berbasis sesi akan cocok dengan infrastruktur saya yang ada.)
Pekka
sumber
13
@Aniel, terima kasih untuk hasil editnya. "Aku lebih banyak kata kerja" adalah pelesetan yang disengaja, tapi aku membiarkannya, lebih mudah dibaca sekarang. :)
Pekka
1
BTW, tentang deskripsi kesalahan. Saya berakhir dengan menempatkan deskripsi kesalahan ke header respons. Cukup tambahkan tajuk bernama 'Deskripsi Kesalahan'.
Andrii Muzychuk
Ini lebih mirip pertanyaan keamanan aplikasi. Keamanan aplikasi bukan tentang REST.
Nazar Merza
@NazarMerza bagaimana 1., 2. dan 3. pertanyaan keamanan aplikasi?
Pekka

Jawaban:

621

Saya memperhatikan pertanyaan ini beberapa hari terlambat, tetapi saya merasa bahwa saya dapat menambahkan beberapa wawasan. Saya harap ini dapat membantu usaha Anda yang tenang.


Butir 1: Apakah saya memahaminya dengan benar?

Anda mengerti benar. Itu adalah representasi yang benar dari arsitektur yang tenang. Anda mungkin menemukan matriks berikut dari Wikipedia sangat membantu dalam mendefinisikan kata benda dan kata kerja Anda:


Ketika berhadapan dengan Koleksi URI seperti:http://example.com/resources/

  • DAPATKAN : Daftarkan anggota koleksi, lengkapi dengan URI anggota mereka untuk navigasi lebih lanjut. Misalnya, daftarkan semua mobil untuk dijual.

  • PUT : Arti didefinisikan sebagai "ganti seluruh koleksi dengan koleksi lain".

  • POST : Buat entri baru dalam koleksi tempat ID ditugaskan secara otomatis oleh koleksi. ID yang dibuat biasanya dimasukkan sebagai bagian dari data yang dikembalikan oleh operasi ini.

  • HAPUS : Arti didefinisikan sebagai "hapus seluruh koleksi".


Ketika berhadapan dengan Anggota URI seperti:http://example.com/resources/7HOU57Y

  • DAPATKAN : Ambil representasi dari anggota koleksi yang dialamatkan yang dinyatakan dalam jenis MIME yang sesuai.

  • PUT : Perbarui anggota koleksi yang dialamatkan atau buat dengan ID yang ditentukan.

  • POST : Memperlakukan anggota yang dialamatkan sebagai koleksi dengan haknya sendiri dan menciptakan bawahan baru untuknya.

  • HAPUS : Hapus anggota koleksi yang dialamatkan.


Butir 2: Saya butuh lebih banyak kata kerja

Secara umum, ketika Anda berpikir Anda membutuhkan lebih banyak kata kerja, itu sebenarnya bisa berarti bahwa sumber daya Anda perlu diidentifikasi kembali. Ingatlah bahwa di REST Anda selalu bertindak berdasarkan sumber daya, atau kumpulan sumber daya. Apa yang Anda pilih sebagai sumber daya cukup penting untuk definisi API Anda.

Aktifkan / Nonaktifkan Login : Jika Anda membuat sesi baru, maka Anda mungkin ingin mempertimbangkan "sesi" sebagai sumber. Untuk membuat sesi baru, gunakan POST http://example.com/sessions/dengan kredensial di badan. Untuk kedaluwarsa, gunakan PUT atau DELETE (mungkin tergantung pada apakah Anda ingin menyimpan riwayat sesi) http://example.com/sessions/SESSION_ID.

Ubah Kata Sandi: Kali ini sumber dayanya adalah "pengguna". Anda memerlukan PUT untuk http://example.com/users/USER_IDmemasukkan kata sandi lama dan baru di dalam tubuh. Anda bertindak berdasarkan sumber daya "pengguna", dan kata sandi perubahan hanyalah permintaan pembaruan. Ini sangat mirip dengan pernyataan UPDATE dalam database relasional.

Insting saya adalah melakukan panggilan GET ke URL /api/users/1/activate_login

Ini bertentangan dengan prinsip REST yang sangat inti: Penggunaan kata kerja HTTP yang benar. Setiap permintaan GET tidak boleh meninggalkan efek samping.

Misalnya, permintaan GET tidak boleh membuat sesi pada basis data, mengembalikan cookie dengan ID Sesi baru, atau meninggalkan residu di server. Kata kerja GET seperti pernyataan SELECT dalam mesin basis data. Ingat bahwa respons terhadap permintaan apa pun dengan kata kerja GET harus tembolok ketika diminta dengan parameter yang sama, seperti ketika Anda meminta halaman web statis.


Butir 3: Cara mengembalikan pesan dan kode kesalahan

Pertimbangkan kode status HTTP 4xx atau 5xx sebagai kategori kesalahan. Anda bisa menguraikan kesalahan dalam tubuh.

Gagal Tersambung ke Database: / Login Database Salah : Secara umum Anda harus menggunakan 500 kesalahan untuk jenis kesalahan ini. Ini adalah kesalahan sisi server. Klien tidak melakukan kesalahan. 500 kesalahan biasanya dianggap "retryable". yaitu klien dapat mencoba kembali permintaan yang sama persis, dan berharap itu berhasil setelah masalah server diselesaikan. Tentukan detail dalam tubuh, sehingga klien akan dapat memberikan beberapa konteks kepada kita manusia.

Kategori kesalahan lainnya adalah keluarga 4xx, yang secara umum menunjukkan bahwa klien melakukan kesalahan. Secara khusus, kategori kesalahan ini biasanya menunjukkan kepada klien bahwa tidak perlu mencoba kembali permintaan sebagaimana adanya, karena itu akan terus gagal secara permanen. yaitu klien perlu mengubah sesuatu sebelum mencoba kembali permintaan ini. Misalnya, "Sumberdaya tidak ditemukan" (HTTP 404) atau "Kesalahan Permintaan" (HTTP 400) kesalahan akan jatuh dalam kategori ini.


Butir 4: Cara melakukan otentikasi

Seperti yang ditunjukkan pada poin 1, alih-alih mengautentikasi pengguna, Anda mungkin ingin berpikir tentang membuat sesi. Anda akan dikembalikan "ID Sesi" baru, bersama dengan kode status HTTP yang sesuai (200: Akses Diberikan atau 403: Akses Ditolak).

Anda kemudian akan bertanya kepada server RESTful Anda: "Bisakah Anda DAPATKAN saya sumber daya untuk ID Sesi ini?".

Tidak ada mode terautentikasi - REST tidak memiliki kewarganegaraan: Anda membuat sesi, Anda meminta server untuk memberi Anda sumber daya menggunakan ID Sesi ini sebagai parameter, dan pada logout Anda membatalkan atau mengakhiri sesi.

Daniel Vassallo
sumber
6
Sangat bagus, namun penggunaan Anda PUTuntuk mengubah kata sandi mungkin salah; PUTmembutuhkan seluruh sumber daya, jadi Anda harus mengirim semua atribut pengguna untuk mematuhi HTTP (dan karenanya dengan HATEOAS REST). Sebaliknya, untuk hanya mengubah kata sandi yang harus digunakan PATCHatau POST.
Lawrence Dol
1
Saya pikir posting ini akan sempurna jika Anda memperluas lebih lanjut tentang apa "POST: Memperlakukan anggota yang dialamatkan sebagai koleksi dalam dirinya sendiri dan menciptakan bawahan baru dari itu." cara. - Saya menemukan apa artinya dengan Googling - ini merupakan pengecualian untuk jawaban Anda yang luar biasa.
Martin Konecny
6
Saya tidak setuju dengan kalimat terakhir. Anda menjelaskan bagaimana REST tidak memiliki kewarganegaraan. Masuk untuk membuat sesi, lalu keluar untuk mengakhiri sesi setelah melakukan beberapa pekerjaan adalah contoh terbaik dari API stateful.
Brandon
1
"Ini bertentangan dengan prinsip REST yang sangat inti: Penggunaan kata kerja HTTP yang benar. Setiap permintaan GET tidak boleh meninggalkan efek samping." - Bagaimana jika Anda ingin mempertahankan jumlah hit untuk sumber daya?
bobbyalex
1
Artikel ini harus menjawab pertanyaan Anda. saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices
java_geek
79

Sederhananya, Anda melakukan ini sepenuhnya mundur.

Anda tidak boleh mendekati ini dari URL apa yang harus Anda gunakan. URL secara efektif akan datang "gratis" setelah Anda memutuskan sumber daya apa yang diperlukan untuk sistem Anda DAN bagaimana Anda akan mewakili sumber daya tersebut, dan interaksi antara sumber daya dan keadaan aplikasi.

Mengutip Roy Fielding

REST API harus menghabiskan hampir semua upaya deskriptifnya dalam mendefinisikan jenis media yang digunakan untuk mewakili sumber daya dan status aplikasi mengemudi, atau dalam mendefinisikan nama hubungan yang diperluas dan / atau mark-up yang memungkinkan hypertext untuk jenis media standar yang ada. Upaya apa pun yang digunakan untuk menjelaskan metode apa yang digunakan pada URI yang diminati harus sepenuhnya ditentukan dalam ruang lingkup aturan pemrosesan untuk jenis media (dan, dalam banyak kasus, sudah ditentukan oleh jenis media yang ada). [Kegagalan di sini menyiratkan bahwa informasi out-of-band mendorong interaksi bukannya hiperteks.]

Orang-orang selalu mulai dengan URI dan berpikir ini adalah solusinya, dan kemudian mereka cenderung kehilangan konsep kunci dalam arsitektur REST, terutama, seperti dikutip di atas, "Kegagalan di sini menyiratkan bahwa informasi out-of-band mendorong interaksi daripada hiperteks. "

Sejujurnya, banyak yang melihat sekelompok URI dan beberapa GET dan PUT dan POST dan berpikir REST itu mudah. SISA tidak mudah. RPC melalui HTTP mudah, memindahkan gumpalan data bolak-balik yang diproksi melalui payload HTTP mudah. REST, bagaimanapun, melampaui itu. REST adalah protokol agnostik. HTTP sangat populer dan tepat untuk sistem REST.

REST hidup dalam jenis media, definisi mereka, dan bagaimana aplikasi mendorong tindakan yang tersedia untuk sumber daya tersebut melalui hypertext (tautan, secara efektif).

Ada pandangan berbeda tentang tipe media dalam sistem REST. Beberapa mendukung muatan aplikasi tertentu, sementara yang lain suka mengangkat jenis media yang ada ke peran yang sesuai untuk aplikasi. Misalnya, di satu sisi Anda memiliki skema XML khusus yang dirancang sesuai untuk aplikasi Anda dibandingkan menggunakan sesuatu seperti XHTML sebagai representasi Anda, mungkin melalui Microformats dan mekanisme lainnya.

Kedua pendekatan memiliki tempat masing-masing, saya pikir, XHTML bekerja sangat baik dalam skenario yang tumpang tindih dengan web yang digerakkan manusia dan mesin, sedangkan yang sebelumnya, tipe data yang lebih spesifik, saya merasa lebih mudah memfasilitasi interaksi mesin ke mesin. Saya menemukan peningkatan format komoditas dapat membuat negosiasi konten berpotensi sulit. "application / xml + yourresource" jauh lebih spesifik sebagai jenis media daripada "application / xhtml + xml", karena yang terakhir dapat berlaku untuk banyak muatan yang mungkin atau mungkin bukan sesuatu yang benar-benar diminati oleh klien mesin, juga tidak bisa tentukan tanpa introspeksi.

Namun, XHTML bekerja dengan sangat baik (jelas) di web manusia di mana browser web dan rendering sangat penting.

Aplikasi Anda akan memandu Anda dalam keputusan semacam itu.

Bagian dari proses merancang sistem REST adalah menemukan sumber daya kelas satu di sistem Anda, bersama dengan turunannya, sumber daya pendukung yang diperlukan untuk mendukung operasi pada sumber daya primer. Setelah sumber daya ditemukan, maka representasi sumber daya tersebut, serta diagram negara menunjukkan aliran sumber daya melalui hypertext dalam representasi karena tantangan berikutnya.

Ingatlah bahwa setiap representasi sumber daya, dalam sistem hypertext, menggabungkan representasi sumber daya aktual bersama dengan transisi negara yang tersedia untuk sumber daya. Anggap setiap sumber daya simpul dalam grafik, dengan tautannya adalah garis yang meninggalkan simpul itu ke negara lain. Tautan ini memberi tahu klien tidak hanya apa yang dapat dilakukan, tetapi apa yang harus dilakukan oleh mereka (karena tautan yang baik menggabungkan URI dan jenis media yang diperlukan).

Misalnya, Anda mungkin memiliki:

<link href="http://example.com/users" rel="users" type="application/xml+usercollection"/>
<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>

Dokumentasi Anda akan berbicara tentang bidang rel bernama "pengguna", dan jenis media "aplikasi / xml + pengguna Anda".

Tautan ini mungkin tampak berlebihan, mereka semua berbicara dengan URI yang sama, cukup banyak. Tapi ternyata tidak.

Ini karena untuk relasi "pengguna", tautan itu berbicara tentang koleksi pengguna, dan Anda dapat menggunakan antarmuka seragam untuk bekerja dengan koleksi (GET untuk mengambil semuanya, HAPUS untuk menghapus semuanya, dll.)

Jika Anda POST ke URL ini, Anda harus melewati dokumen "application / xml + usercollection", yang mungkin hanya akan berisi satu instance pengguna dalam dokumen sehingga Anda dapat menambahkan pengguna, atau tidak, mungkin, untuk menambahkan beberapa di sekali. Mungkin dokumentasi Anda akan menyarankan agar Anda cukup mengoper satu jenis pengguna saja, bukan koleksi.

Anda dapat melihat apa yang dibutuhkan aplikasi untuk melakukan pencarian, seperti yang ditentukan oleh tautan "pencarian" dan itu adalah tipe media. Dokumentasi untuk jenis media pencarian akan memberi tahu Anda bagaimana ini berperilaku, dan apa yang diharapkan sebagai hasil.

Namun, yang dapat dibawa pulang di sini adalah URI sendiri pada dasarnya tidak penting. Aplikasi ini mengendalikan URI, bukan klien. Di luar beberapa 'titik masuk', klien Anda harus mengandalkan URI yang disediakan oleh aplikasi untuk pekerjaannya.

Klien perlu tahu cara memanipulasi dan menafsirkan jenis media, tetapi tidak terlalu peduli ke mana ia pergi.

Kedua tautan ini secara semantik identik di mata klien:

<link href="http://example.com/users?search" rel="search" type="application/xml+usersearchcriteria"/>
<link href="http://example.com/AW163FH87SGV" rel="search" type="application/xml+usersearchcriteria"/>

Jadi, fokuslah pada sumber daya Anda. Berfokuslah pada transisi keadaan mereka dalam aplikasi dan bagaimana hal itu dicapai dengan terbaik.

Will Hartung
sumber
1
Terima kasih Will atas jawaban yang sangat dalam ini. Beberapa poin diambil. Saya menyadari bahwa perencanaan dari "seperti apa URL itu" melakukannya dengan cara sebaliknya, dan saya juga merencanakan dari sisi sumber daya. Memiliki URL untuk dimainkan membuat saya lebih mudah memahami konsepnya. Ini bisa jadi persyaratan saya bisa bertemu dengan sistem yang tidak 100% prinsip follow SISA seperti yang Anda mendefinisikannya di sini. Saya akan menggambar daftar lengkap persyaratan untuk setiap jenis sumber daya, saya kira saya akan dapat memutuskan itu. Bersulang.
Pekka
30

re 1 : Sejauh ini terlihat bagus. Ingatlah untuk mengembalikan URI pengguna yang baru dibuat di tajuk "Lokasi:" sebagai bagian dari respons terhadap POST, bersama dengan kode status "201 Dibuat".

kembali 2: Aktivasi melalui GET adalah ide yang buruk, dan termasuk kata kerja dalam URI adalah bau desain. Anda mungkin ingin mempertimbangkan untuk mengembalikan formulir pada GET. Dalam aplikasi Web, ini akan menjadi formulir HTML dengan tombol kirim; dalam kasus penggunaan API, Anda mungkin ingin mengembalikan representasi yang berisi URI ke PUT untuk mengaktifkan akun. Tentu saja Anda dapat memasukkan URI ini dalam respons POST kepada / pengguna juga. Menggunakan PUT akan memastikan permintaan Anda idempoten, yaitu permintaan dapat dikirim kembali dengan aman jika klien tidak yakin tentang kesuksesan. Secara umum, pikirkan tentang sumber daya apa yang Anda bisa ubah menjadi kata kerja (semacam "kata benda dari kata kerja"). Tanyakan pada diri sendiri metode apa yang digunakan untuk tindakan spesifik Anda. Misalnya change_password -> PUT; nonaktifkan -> mungkin HAPUS; add_credit -> mungkin POST atau PUT.

kembali 3. Jangan membuat kode status baru, kecuali Anda yakin kode itu sangat umum sehingga layak distandarisasi secara global. Berusaha keras untuk menggunakan kode status paling tepat yang tersedia (baca tentang semuanya di RFC 2616). Sertakan informasi tambahan di badan tanggapan. Jika Anda benar-benar yakin ingin menemukan kode status baru, pikirkan lagi; jika Anda masih percaya demikian, pastikan untuk setidaknya memilih kategori yang tepat (1xx -> OK, 2xx -> informasi, 3xx -> pengalihan; 4xx-> kesalahan klien, 5xx -> kesalahan server). Apakah saya menyebutkan bahwa menciptakan kode status baru adalah ide yang buruk?

kembali 4. Jika memungkinkan, gunakan kerangka kerja otentikasi yang dibangun ke dalam HTTP. Lihat cara Google melakukan otentikasi di GData. Secara umum, jangan letakkan kunci API di URI Anda. Cobalah untuk menghindari sesi untuk meningkatkan skalabilitas dan mendukung caching - jika respons terhadap permintaan berbeda karena sesuatu yang telah terjadi sebelumnya, Anda biasanya mengikat diri Anda ke instance proses server tertentu. Jauh lebih baik untuk mengubah status sesi menjadi status klien (misalnya menjadikannya bagian dari permintaan berikutnya) atau membuatnya eksplisit dengan mengubahnya menjadi status sumber daya (server), yaitu memberikan URI sendiri.

Stefan Tilkov
sumber
Bisakah Anda mendiskusikan mengapa tidak memasukkan kunci API ke URL? Apakah karena mereka terlihat di log proxy? Bagaimana jika tombolnya sementara, berdasarkan waktu? Bagaimana jika HTTPS digunakan?
MikeSchinkel
4
Selain melanggar roh (URI harus mengidentifikasi hal-hal), konsekuensi utama adalah bahwa hal itu merusak caching.
Stefan Tilkov
22

1. Anda punya ide yang tepat tentang bagaimana merancang sumber daya Anda, IMHO. Saya tidak akan mengubah apa pun.

2. Daripada mencoba memperluas HTTP dengan lebih banyak kata kerja, pertimbangkan apa kata kerja yang Anda usulkan dapat direduksi dalam hal metode dan sumber daya HTTP dasar. Misalnya, alih-alih activate_loginkata kerja, Anda dapat mengatur sumber daya seperti: /api/users/1/login/activeyang merupakan boolean sederhana. Untuk mengaktifkan login, hanya PUTdokumen di sana yang mengatakan 'benar' atau 1 atau apa pun. Untuk menonaktifkan, PUTdokumen di sana yang kosong atau mengatakan 0 atau salah.

Demikian pula, untuk mengubah atau mengatur kata sandi, cukup lakukan PUTs /api/users/1/password.

Setiap kali Anda perlu menambahkan sesuatu (seperti kredit), pikirkan POSTs. Misalnya, Anda dapat melakukan a POSTke sumber daya seperti /api/users/1/creditsdengan badan yang berisi jumlah kredit untuk ditambahkan. A PUTpada sumber daya yang sama dapat digunakan untuk menimpa nilai daripada menambah. A POSTdengan angka negatif di dalam tubuh akan berkurang, dan seterusnya.

3. Saya sangat menyarankan untuk tidak memperpanjang kode status HTTP dasar. Jika Anda tidak dapat menemukan yang cocok dengan situasi Anda, pilih yang terdekat dan letakkan detail kesalahan di badan respons. Juga, ingat bahwa header HTTP dapat diperluas; aplikasi Anda dapat menentukan semua tajuk khusus yang Anda sukai. Salah satu aplikasi yang saya kerjakan, misalnya, dapat mengembalikan 404 Not Founddalam beberapa keadaan. Alih-alih membuat klien mem-parsing badan respons karena alasan itu, kami hanya menambahkan header baru X-Status-Extended, yang berisi ekstensi kode status milik kami. Jadi, Anda mungkin melihat respons seperti:

HTTP/1.1 404 Not Found    
X-Status-Extended: 404.3 More Specific Error Here

Dengan cara itu klien HTTP seperti browser web masih akan tahu apa yang harus dilakukan dengan kode 404 biasa, dan klien HTTP yang lebih canggih dapat memilih untuk melihat X-Status-Extendedheader untuk informasi yang lebih spesifik.

4. Untuk otentikasi, saya sarankan menggunakan otentikasi HTTP jika Anda bisa. Tapi IMHO tidak ada yang salah dengan menggunakan otentikasi berbasis cookie jika itu lebih mudah bagi Anda.

friedo
sumber
4
Gagasan rapi menggunakan sumber daya "diperluas" untuk melakukan hal-hal ke bagian yang lebih kecil dari sumber daya yang lebih besar.
womble
1
Cookie valid dalam HTTP / REST, tetapi server tidak boleh menyimpan cookie sebagai status (jadi bukan sebagai sesi). Cookie dapat menyimpan nilai seperti HMAC, yang dapat dibongkar tanpa melihat keadaan di tempat lain.
Bruce Alderson
14

REST Basics

REST memiliki batasan antarmuka yang seragam, yang menyatakan bahwa klien REST harus bergantung pada standar alih-alih detail spesifik aplikasi dari layanan REST yang sebenarnya, sehingga klien REST tidak akan berhenti karena perubahan kecil, dan itu mungkin akan dapat digunakan kembali.

Jadi ada kontrak antara klien REST dan layanan REST. Jika Anda menggunakan HTTP sebagai protokol yang mendasarinya, maka standar berikut adalah bagian dari kontrak:

  • HTTP 1.1
    • definisi metode
    • definisi kode status
    • header kontrol cache
    • menerima dan header tipe konten
    • header auth
  • IRI (utf8 URI )
  • tubuh (pilih satu)
  • hyperlink
    • apa yang harus mengandung mereka (pilih satu)
      • mengirim header tautan
      • mengirimkan respons hypermedia, misalnya html, atom + xml, hal + json, ld + json & hydra, dll ...
    • semantik
      • menggunakan hubungan tautan IANA dan mungkin hubungan tautan ubahsuaian
      • menggunakan aplikasi khusus RDF vocab

REST memiliki batasan stateless, yang menyatakan bahwa komunikasi antara layanan REST dan klien harus stateless. Ini berarti bahwa layanan REST tidak dapat mempertahankan status klien, sehingga Anda tidak dapat memiliki penyimpanan sesi sisi server. Anda harus mengotentikasi setiap permintaan tunggal. Jadi misalnya HTTP auth dasar (bagian dari standar HTTP) tidak apa-apa, karena mengirimkan nama pengguna dan kata sandi dengan setiap permintaan.

Untuk menjawab pertanyaan Anda

  1. Ya bisa.

    Sebagai tambahan, klien tidak peduli dengan struktur IRI, mereka peduli dengan semantik, karena mereka mengikuti tautan yang memiliki hubungan tautan atau atribut data yang terhubung (RDF).

    Satu-satunya hal penting tentang IRI, bahwa satu IRI harus mengidentifikasi hanya satu sumber daya saja. Ini diizinkan untuk satu sumber daya, seperti pengguna, untuk memiliki banyak IRI yang berbeda.

    Sangat sederhana mengapa kita menggunakan IRI yang bagus seperti /users/123/password; jauh lebih mudah untuk menulis logika perutean di server ketika Anda memahami IRI hanya dengan membacanya.

  2. Anda memiliki lebih banyak kata kerja, seperti PUT, PATCH, OPTIONS, dan bahkan lebih banyak lagi, tetapi Anda tidak membutuhkan lebih dari itu ... Alih-alih menambahkan kata kerja baru Anda harus belajar cara menambahkan sumber daya baru.

    activate_login -> PUT /login/active true deactivate_login -> PUT /login/active false change_password -> PUT /user/xy/password "newpass" add_credit -> POST /credit/raise {details: {}}

    (Login tidak masuk akal dari perspektif REST, karena kendala stateless.)

  3. Pengguna Anda tidak peduli mengapa masalah itu ada. Mereka hanya ingin tahu apakah ada kesuksesan atau kesalahan, dan mungkin pesan kesalahan yang dapat mereka pahami, misalnya: "Maaf, tetapi kami tidak dapat menyimpan pos Anda.", Dll ...

    Header status HTTP adalah header standar Anda. Segala sesuatu yang lain harus ada dalam tubuh saya pikir. Header tunggal tidak cukup untuk menjelaskan misalnya pesan kesalahan multibahasa yang terperinci.

  4. Batasan stateless (bersama dengan cache dan batasan sistem layered) memastikan bahwa layanan berskala baik. Anda tentu tidak ingin mempertahankan jutaan sesi di server, ketika Anda dapat melakukan hal yang sama pada klien ...

    Klien pihak ke-3 mendapat token akses jika pengguna memberikan akses kepadanya menggunakan klien utama. Setelah itu, klien pihak ke-3 mengirimkan token akses dengan setiap permintaan. Ada solusi yang lebih rumit, misalnya Anda dapat menandatangani setiap permintaan, dll. Untuk detail lebih lanjut, periksa manual OAuth.

Literatur terkait

inf3rno
sumber
11

Untuk contoh yang Anda nyatakan saya akan menggunakan yang berikut:

aktifkan_login

POST /users/1/activation

deactivate_login

DELETE /users/1/activation

ganti kata sandi

PUT /passwords (ini mengasumsikan pengguna diautentikasi)

add_credit

POST /credits (ini mengasumsikan pengguna diautentikasi)

Untuk kesalahan Anda akan mengembalikan kesalahan di badan dalam format yang Anda dapatkan permintaannya, jadi jika Anda menerima:

DELETE /users/1.xml

Anda akan mengirim respons kembali dalam XML, hal yang sama akan berlaku untuk JSON dll ...

Untuk otentikasi Anda harus menggunakan otentikasi http.

jonnii
sumber
1
Saya tidak akan menggunakan createsebagai bagian dari URI (ingat bahwa URI harus menjadi kata benda, dan metode HTTP harus berupa kata kerja yang beroperasi pada kata benda itu.) Sebaliknya, saya akan memiliki sumber daya seperti /users/1/activeyang bisa menjadi boolean sederhana, dan itu bisa menjadi atur dengan PUTting 1 atau 0 ke sumber daya itu.
friedo
Anda benar, saya mengeluarkan / create. Seharusnya hanya posting ke sumber tunggal.
Jonnii
3
Saya juga tidak akan menggunakan activationURI, kecuali jika Anda secara eksplisit akan memanipulasi dan mengelola sumber daya dengan nama /users/1/activation. Apa manfaat dari GET? Apa yang dilakukan PUT? Rasanya bagi saya bahwa Anda melakukan verifikasi URI. Juga, seperti untuk negosiasi tipe konten, itu juga seringkali lebih baik ditinggalkan dari URI dan dimasukkan ke dalam header, seperti Accept.
Cheeso
6
  1. Gunakan posting ketika Anda tidak tahu bagaimana URI sumber daya baru akan terlihat (Anda membuat pengguna baru, aplikasi akan menetapkan pengguna baru itu id), PUT untuk memperbarui atau membuat sumber daya yang Anda tahu bagaimana mereka akan diwakili (contoh : PUT / myfiles/thisismynewfile.txt)
  2. mengembalikan deskripsi kesalahan di badan pesan
  3. Anda dapat menggunakan otentikasi HTTP (jika cukup) Layanan Web harus berupa stateles
Arjan
sumber
5

Saya akan menyarankan (sebagai pass pertama) yang PUTseharusnya hanya digunakan untuk memperbarui entitas yang ada. POSTharus digunakan untuk membuat yang baru. yaitu

/api/users     when called with PUT, creates user record

tidak terasa benar bagi saya. Namun, sisa bagian pertama Anda (penggunaan kembali kata kerja) terlihat logis.

Brian Agnew
sumber
mungkin seseorang mengira ini bukan jawaban untuk pertanyaannya
lubos hasko
6
Pandangan saya tentang PUT versus POST untuk membuat entitas baru adalah dengan menggunakan PUT ketika pemanggil mengontrol nama sumber daya, sehingga Anda dapat PUT ke sumber daya yang tepat dan POST ketika callee mengontrol nama sumber daya baru (seperti dalam contoh di sini).
SteveD
5

Verbose, tetapi disalin dari spesifikasi metode HTTP 1.1 di http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html

9.3 DAPATKAN

Metode GET berarti mengambil informasi apa pun (dalam bentuk entitas) yang diidentifikasi oleh Request-URI. Jika Permintaan-URI mengacu pada proses produksi data, itu adalah data yang dihasilkan yang akan dikembalikan sebagai entitas dalam respons dan bukan teks sumber proses, kecuali jika teks tersebut merupakan output dari proses tersebut.

Semantik dari metode GET berubah menjadi "GET bersyarat" jika pesan permintaan menyertakan If-Modified-Sejak, If-Unmodified-Sejak, If-Match, If-None-Match, atau If-Range header. Metode GET bersyarat meminta agar entitas ditransfer hanya dalam keadaan yang dijelaskan oleh bidang header kondisional. Metode GET bersyarat dimaksudkan untuk mengurangi penggunaan jaringan yang tidak perlu dengan memungkinkan entitas yang di-cache untuk di-refresh tanpa memerlukan beberapa permintaan atau mentransfer data yang sudah dipegang oleh klien.

Semantik metode GET berubah menjadi "GET sebagian" jika pesan permintaan menyertakan bidang header Rentang. Sebagian GET meminta agar hanya sebagian entitas yang ditransfer, seperti yang dijelaskan dalam bagian 14.35. Metode GET parsial dimaksudkan untuk mengurangi penggunaan jaringan yang tidak perlu dengan memungkinkan sebagian entitas yang diambil kembali diselesaikan tanpa mentransfer data yang sudah dipegang oleh klien.

Respons terhadap permintaan GET dapat di-cache jika dan hanya jika memenuhi persyaratan untuk cache HTTP yang dijelaskan di bagian 13.

Lihat bagian 15.1.3 untuk pertimbangan keamanan saat digunakan untuk formulir.

9,5 POST

Metode POST digunakan untuk meminta server asal menerima entitas yang dilampirkan dalam permintaan sebagai bawahan baru dari sumber daya yang diidentifikasi oleh Request-URI di Request-Line. POST dirancang untuk memungkinkan metode yang seragam untuk mencakup fungsi-fungsi berikut:

  - Annotation of existing resources;
  - Posting a message to a bulletin board, newsgroup, mailing list,
    or similar group of articles;
  - Providing a block of data, such as the result of submitting a
    form, to a data-handling process;
  - Extending a database through an append operation.

Fungsi aktual yang dilakukan oleh metode POST ditentukan oleh server dan biasanya tergantung pada Request-URI. Entitas yang diposting lebih rendah dari URI dengan cara yang sama dengan file yang berada di bawah direktori yang memuatnya, artikel berita lebih rendah dari newsgroup yang diposkan, atau catatan lebih rendah dari database.

Tindakan yang dilakukan oleh metode POST mungkin tidak menghasilkan sumber daya yang dapat diidentifikasi oleh URI. Dalam hal ini, 200 (OK) atau 204 (Tidak Ada Konten) adalah status respons yang sesuai, tergantung pada apakah respons tersebut termasuk atau tidak entitas yang menggambarkan hasil.

Jika sumber daya telah dibuat di server asal, respons HARUS 201 (Dibuat) dan berisi entitas yang menggambarkan status permintaan dan merujuk ke sumber daya baru, dan header Lokasi (lihat bagian 14.30).

Respons terhadap metode ini tidak dapat di-cache, kecuali jika responsnya menyertakan bidang Cache-Control atau Expire header yang sesuai. Namun, respons 303 (Lihat Lainnya) dapat digunakan untuk mengarahkan agen pengguna untuk mengambil sumber daya yang dapat di-cache.

Permintaan POST HARUS mematuhi persyaratan pengiriman pesan yang ditetapkan dalam bagian 8.2.

Lihat bagian 15.1.3 untuk pertimbangan keamanan.

9.6 PUT

Metode PUT meminta entitas terlampir disimpan di bawah Request-URI yang disediakan. Jika Request-URI mengacu pada sumber daya yang sudah ada, entitas terlampir HARUS dianggap sebagai versi modifikasi dari yang berada di server asal. Jika Request-URI tidak menunjuk ke sumber daya yang ada, dan bahwa URI mampu didefinisikan sebagai sumber daya baru oleh agen pengguna yang meminta, server asal dapat membuat sumber daya dengan URI itu. Jika sumber daya baru dibuat, server asal HARUS memberi tahu agen pengguna melalui respons 201 (Dibuat). Jika sumber daya yang ada diubah, kode respons 200 (OK) atau 204 (Tidak Ada Konten) HARUS dikirim untuk mengindikasikan keberhasilan penyelesaian permintaan. Jika sumber daya tidak dapat dibuat atau dimodifikasi dengan Request-URI, respons kesalahan yang sesuai HARUS diberikan yang mencerminkan sifat masalah. Penerima entitas TIDAK HARUS mengabaikan header Konten- * (misalnya Content-Range) yang tidak dimengerti atau diimplementasikan dan HARUS mengembalikan respons 501 (Tidak Diimplementasikan) dalam kasus-kasus seperti itu.

Jika permintaan melewati cache dan Request-URI mengidentifikasi satu atau lebih entitas yang di-cache saat ini, entri-entri tersebut HARUS diperlakukan sebagai basi. Respons terhadap metode ini tidak dapat di-cache.

Perbedaan mendasar antara permintaan POST dan PUT tercermin dalam arti berbeda dari Request-URI. URI dalam permintaan POST mengidentifikasi sumber daya yang akan menangani entitas terlampir. Sumber daya itu bisa berupa proses penerimaan data, gateway ke beberapa protokol lain, atau entitas terpisah yang menerima anotasi. Sebaliknya, URI dalam permintaan PUT mengidentifikasi entitas yang dilampirkan dengan permintaan - agen pengguna tahu apa yang dimaksud dengan URI dan server TIDAK HARUS mencoba menerapkan permintaan tersebut ke sumber daya lain. Jika server ingin agar permintaan diterapkan ke URI yang berbeda,

itu HARUS mengirim respons 301 (Dipindahkan Secara Permanen); agen pengguna MUNGKIN kemudian membuat keputusan sendiri mengenai apakah akan mengarahkan permintaan atau tidak.

Satu sumber daya DAPAT diidentifikasi oleh banyak URI yang berbeda. Misalnya, sebuah artikel mungkin memiliki URI untuk mengidentifikasi "versi saat ini" yang terpisah dari URI yang mengidentifikasi setiap versi tertentu. Dalam hal ini, permintaan PUT pada URI umum dapat mengakibatkan beberapa URI lain ditentukan oleh server asal.

HTTP / 1.1 tidak mendefinisikan bagaimana metode PUT mempengaruhi keadaan server asal.

Permintaan PUT HARUS mematuhi persyaratan pengiriman pesan yang ditetapkan dalam bagian 8.2.

Kecuali ditentukan lain untuk header entitas tertentu, header entitas dalam permintaan PUT HARUS diterapkan pada sumber daya yang dibuat atau dimodifikasi oleh PUT.

9.7 HAPUS

Metode DELETE meminta server asal menghapus sumber yang diidentifikasi oleh Request-URI. Metode ini DAPAT ditimpa oleh intervensi manusia (atau cara lain) pada server asal. Klien tidak dapat dijamin bahwa operasi telah dilakukan, bahkan jika kode status yang dikembalikan dari server asal menunjukkan bahwa tindakan telah berhasil diselesaikan. Namun, server TIDAK HARUS menunjukkan keberhasilan kecuali, pada saat respon diberikan, itu bermaksud untuk menghapus sumber daya atau memindahkannya ke lokasi yang tidak dapat diakses.

Respons yang berhasil HARUS menjadi 200 (OK) jika responsnya mencakup entitas yang menggambarkan status, 202 (Diterima) jika tindakan belum diberlakukan, atau 204 (Tidak Ada Konten) jika tindakan telah diberlakukan tetapi respons tidak termasuk sebuah entitas.

Jika permintaan melewati cache dan Request-URI mengidentifikasi satu atau lebih entitas yang di-cache saat ini, entri-entri tersebut HARUS diperlakukan sebagai basi. Respons terhadap metode ini tidak dapat di-cache.

gahooa
sumber
2

Tentang kode pengembalian REST: salah mencampur kode protokol HTTP dan hasil REST.

Namun, saya melihat banyak implementasi yang memadukannya, dan banyak pengembang mungkin tidak setuju dengan saya.

Kode pengembalian HTTP terkait dengan HTTP Requestitu sendiri. Panggilan REST dilakukan menggunakan permintaan Hypertext Transfer Protocol dan berfungsi pada tingkat yang lebih rendah daripada metode REST yang dipanggil itu sendiri. REST adalah konsep / pendekatan, dan outputnya adalah hasil bisnis / logis , sedangkan kode hasil HTTP adalah transportasi .

Misalnya, mengembalikan "404 Tidak ditemukan" ketika Anda memanggil / pengguna / bingung, karena itu dapat berarti:

  • URI salah (HTTP)
  • Tidak ada pengguna yang ditemukan (REST)

"403 Dilarang / Akses Ditolak" dapat berarti:

  • Diperlukan izin khusus. Browser dapat menanganinya dengan menanyakan pengguna / kata sandi. (HTTP)
  • Izin akses yang salah dikonfigurasi di server. (HTTP)
  • Anda perlu diautentikasi (REST)

Dan daftar tersebut dapat berlanjut dengan '500 Server error "(kesalahan Apache / Nginx HTTP dilemparkan atau kesalahan kendala bisnis di REST) ​​atau kesalahan HTTP lainnya dll ...

Dari kode, sulit untuk memahami apa yang menjadi alasan kegagalan, kegagalan HTTP (transportasi) atau kegagalan REST (logis).

Jika permintaan HTTP berhasil dilakukan secara fisik, ia harus selalu mengembalikan 200 kode, terlepas dari apakah catatannya ditemukan atau tidak. Karena sumber daya URI ditemukan dan ditangani oleh server http. Ya, itu mungkin mengembalikan set kosong. Apakah mungkin untuk menerima halaman web kosong dengan 200 sebagai hasil http, kan?

Alih-alih ini, Anda dapat mengembalikan 200 kode HTTP dan cukup JSON dengan array / objek kosong, atau menggunakan bool result / flag sukses untuk menginformasikan tentang status operasi yang dilakukan.

Juga, beberapa penyedia internet dapat mencegat permintaan Anda dan mengembalikan Anda kode 404 http. Ini tidak berarti bahwa data Anda tidak ditemukan, tetapi ada sesuatu yang salah pada tingkat transportasi.

Dari Wiki :

Pada Juli 2004, penyedia telekomunikasi Inggris, BT Group, menyebarkan sistem pemblokiran konten Cleanfeed, yang mengembalikan kesalahan 404 untuk setiap permintaan konten yang diidentifikasi berpotensi ilegal oleh Internet Watch Foundation. ISP lain mengembalikan kesalahan HTTP 403 "dilarang" dalam keadaan yang sama. Praktek mempekerjakan 404 kesalahan palsu sebagai cara untuk menyembunyikan sensor juga telah dilaporkan di Thailand dan Tunisia. Di Tunisia, di mana sensor sangat keras sebelum revolusi 2011, orang menjadi sadar akan sifat kesalahan 404 palsu dan menciptakan karakter imajiner bernama "Ammar 404" yang mewakili "sensor tak terlihat".

Marcodor
sumber