RESTfully desain / login atau / register resource?

122

Saya sedang mendesain aplikasi web dan kemudian berhenti untuk memikirkan bagaimana api saya harus dirancang sebagai layanan web yang tenang. Untuk saat ini, sebagian besar URI saya bersifat generik dan mungkin berlaku untuk berbagai aplikasi web:

GET  /logout   // destroys session and redirects to /
GET  /login    // gets the webpage that has the login form
POST /login    // authenticates credentials against database and either redirects home with a new session or redirects back to /login
GET  /register // gets the webpage that has the registration form
POST /register // records the entered information into database as a new /user/xxx
GET  /user/xxx // gets and renders current user data in a profile view
POST /user/xxx // updates new information about user

Saya merasa saya melakukan banyak kesalahan di sini setelah melihat-lihat SO dan Google.

Dimulai dengan /logout, mungkin karena saya tidak benar-benar GETapa-apa - mungkin lebih tepat untuk POSTmeminta /logout, menghancurkan sesi, dan kemudian GETpengalihan. Dan haruskah /logoutistilah itu tetap ada?

Tentang /logindan /register. Saya dapat mengubah /registerke /registrationtetapi itu tidak mengubah cara kerja layanan saya secara fundamental - jika ada masalah yang lebih dalam.

Saya perhatikan sekarang bahwa saya tidak pernah mengekspos /usersumber daya. Mungkin itu bisa dimanfaatkan entah bagaimana. Misalnya, ambil pengguna myUser:

foo.com/user/myUser

atau

foo.com/user

Pengguna akhir tidak memerlukan verbositas ekstra di URI. Namun, mana yang lebih menarik secara visual?

Saya memperhatikan beberapa pertanyaan lain di SO tentang bisnis REST ini, tetapi saya akan sangat menghargai beberapa panduan tentang apa yang telah saya paparkan di sini jika memungkinkan.

Terima kasih!

MEMPERBARUI:

Saya juga ingin beberapa pendapat tentang:

/user/1

vs.

/user/myUserName
Qcom
sumber

Jawaban:

63

Satu hal yang menonjol khususnya sebagai tidak REST-ful: penggunaan permintaan GET untuk logout.

(dari http://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Safe_methods )

Beberapa metode (misalnya, HEAD, GET, OPTIONS dan TRACE) didefinisikan sebagai aman, yang berarti metode tersebut dimaksudkan hanya untuk pengambilan informasi dan tidak boleh mengubah status server. Dengan kata lain, mereka seharusnya tidak memiliki efek samping, di luar efek yang relatif tidak berbahaya seperti logging, caching, penayangan iklan banner atau peningkatan web counter. [...]

[... H] andling [permintaan GET] oleh server tidak dibatasi secara teknis dengan cara apa pun. Oleh karena itu, pemrograman yang ceroboh atau disengaja dapat menyebabkan perubahan non-sepele pada server. Hal ini tidak disarankan, karena dapat menyebabkan masalah untuk cache Web, mesin telusur, dan agen otomatis lainnya [...]

Untuk logout dan redirect, Anda bisa membuat postingan ke URI logout Anda memberikan respons 303 yang mengarahkan ke halaman setelah logout.

http://en.wikipedia.org/wiki/Post/Redirect/Get

http://en.wikipedia.org/wiki/HTTP_303

Edit untuk mengatasi masalah desain URL:

"Bagaimana cara mendesain sumber daya saya?" adalah pertanyaan penting bagi saya; "bagaimana cara mendesain URL saya?" adalah pertimbangan di dua bidang:

URL yang akan dilihat pengguna seharusnya tidak terlalu jelek dan bermakna jika memungkinkan; jika Anda ingin cookie dikirim dalam permintaan ke beberapa sumber daya tetapi tidak ke yang lain, Anda harus menyusun jalur dan jalur cookie Anda.

Jika JRandomUseringin melihat profilnya sendiri dan Anda ingin URL-nya lebih cantik dari foo.com/user/JRandomUseratau foo.com/user/(JRandom's numeric user id here), Anda dapat membuat URL terpisah hanya agar pengguna melihat informasinya sendiri:

GET foo.com/profile /*examines cookies to figure out who 
                     * is logged in (SomeUser) and then 
                     * displays the same response as a
                     * GET to foo.com/users/SomeUser.
                     */

Saya akan mengklaim ketidaktahuan jauh lebih mudah daripada kebijaksanaan tentang hal ini, tetapi berikut adalah beberapa pertimbangan desain sumber daya:

  1. Konsumen: sumber daya apa yang dimaksudkan untuk dilihat secara langsung di browser, dimuat melalui XHR, atau diakses oleh klien jenis lain?
  2. Akses / identitas: apakah tanggapan bergantung pada cookie atau perujuk?
Ellisbben
sumber
1
Jawaban yang bagus, terima kasih! Jika saya akan menerapkan saran URL terpisah Anda ( GET foo.com/profile/) apakah itu akan menjadi bagian dari, seperti yang disarankan momo, lapisan presentasi? Dengan kata lain, apa tepatnya yang harus GETdikembalikan permintaan itu? Halaman web atau JSON?
Qcom
2
Ah, saya rasa saya mengerti sekarang. Jawaban Momo benar-benar menjelaskan segalanya. Jadi tenang API dibangun untuk memungkinkan beberapa platform untuk GET, POST, PUT, dan DELETEsumber daya. Situs web hanyalah platform lain yang mengakses API. Dengan kata lain, desain URL situs web benar-benar berbeda dari desain RESTful API. Tolong beritahu saya jika saya masih salah haha.
Qcom
Ya, jadikan REST API Anda satu set URL dan situs web Anda menjadi satu set berbeda. Kemudian URL situs web Anda harus mengembalikan HTML + Javascript yang sesuai sehingga halaman akan membuat XmlHttpRequests yang sesuai ke URL API Anda untuk bertindak sebagai klien.
ellisbben
129

RESTful dapat digunakan sebagai pedoman untuk membuat URL, dan Anda dapat membuat sesi dan sumber daya pengguna :

  • GET /session/new mendapatkan halaman web yang memiliki formulir login
  • POST /session mengautentikasi kredensial terhadap database
  • DELETE /session hancurkan sesi dan alihkan ke /
  • GET /users/new mendapatkan halaman web yang memiliki formulir pendaftaran
  • POST /users mencatat informasi yang dimasukkan ke dalam database sebagai / pengguna / xxx baru
  • GET /users/xxx // mendapatkan dan merender data pengguna saat ini dalam tampilan profil
  • POST /users/xxx // memperbarui informasi baru tentang pengguna

Ini bisa jamak atau tunggal (saya tidak yakin mana yang benar). Saya biasanya menggunakan /usershalaman indeks pengguna (seperti yang diharapkan), dan /sessionsuntuk melihat siapa yang masuk (seperti yang diharapkan).

Menggunakan nama di URL alih-alih angka ( /users/43vs. /users/joe) biasanya didorong oleh keinginan untuk lebih bersahabat dengan pengguna atau mesin pencari, bukan persyaratan teknis. Keduanya baik-baik saja, tetapi saya sarankan Anda konsisten.

Saya pikir jika Anda menggunakan register / login / logout atau sign(in|up|out), itu tidak berfungsi dengan baik dengan terminologi yang tenang.

ndp
sumber
6
Hebat! Saya suka bagaimana Anda menyebut sumber daya itu; itu cukup bersih. Meskipun, dari apa yang saya dengar, bukankah menambahkan /newke GET /session/non RESTful? Saya pernah mendengar bahwa kata kerja biasanya diserahkan kepada verba HTTP ( GET, POST, dll).
Qcom
2
@Zach baru bukanlah kata kerja. Dalam hal ini, ini adalah sub-sumber sesi.
Kugel
Bagaimana cara menentukan sesi mana yang akan dihapus di DELETE / session? Curl tidak mengirim cookie atau parameter apa pun dalam permintaan DELETE. Saya berasumsi - hanya untuk menggunakan DELETE / session / sessionId? Pertanyaan lainnya adalah bagaimana mengembalikan id sesi dalam POST / sesi dan dalam format apa.
Tvaroh
9
Istirahat memang merupakan cara untuk membuat diri Anda tidak bahagia dan membuang waktu untuk hal-hal yang tidak penting sama sekali.
Jian Chen
6
Secara pribadi saya tidak suka gagasan memiliki rute yang mengembalikan bentuk (/ baru). Ini memecah pemisahan antara pandangan dan logika bisnis. Tha berkata, tanpa / rute baru, yang disarankan terlihat sempurna.
Scadge
60

Sesi tidak tenang

  • Ya saya tahu. Ini dilakukan, biasanya dengan OAuth, tetapi sesi sebenarnya tidak TENANG. Anda tidak boleh memiliki sumber daya / login / logout terutama karena Anda tidak boleh memiliki sesi.

  • Jika Anda ingin melakukannya, buatlah tenang. Sumber daya adalah kata benda dan / login dan / logout bukan kata benda. Saya akan memilih / session. Ini membuat pembuatan dan penghapusan menjadi tindakan yang lebih alami.

  • POST vs. GET untuk sesi itu mudah. Jika Anda mengirim pengguna / kata sandi sebagai variabel, saya akan menggunakan POST karena saya tidak ingin kata sandi dikirim sebagai bagian dari URI. Ini akan muncul di log dan mungkin diekspos melalui kabel. Anda juga berisiko mengalami kegagalan perangkat lunak pada batasan GET args.

  • Saya biasanya menggunakan Basic Auth atau no Auth dengan layanan REST.

Membuat pengguna

  • Itu satu sumber daya, jadi Anda tidak perlu / mendaftar.

    • POST / pengguna - Membuat pengguna jika pemohon tidak dapat menentukan id
    • PUT / user / xxx - Buat atau perbarui pengguna dengan asumsi Anda mengetahui id sebelumnya
    • GET / pengguna - daftar x id pengguna
    • GET / user / xxx - MENDAPATKAN detail pengguna dengan id xxx
    • HAPUS / pengguna / xxx - Hapus pengguna dengan id xxx
  • Jenis ID yang akan digunakan adalah pertanyaan yang sulit. Anda harus berpikir tentang menegakkan keunikan, tentang penggunaan kembali ID lama yang telah DIHAPUS. Misalnya, Anda tidak ingin menggunakan id tersebut sebagai kunci asing di backend jika id akan didaur ulang (jika memungkinkan). Anda dapat melakukan pencarian untuk konversi id eksternal / internal untuk mengurangi persyaratan backend.

dietbuddha
sumber
6
Inilah jawaban terbaik. / login dan / logout bukanlah sumber daya dan mematahkan gagasan REST.
wle8300
5
Otentikasi! = Sesi
dietbuddha
1
Ya, tesis Fielding menyatakan di bagian 5.1.3 bahwa "status esion [...] disimpan sepenuhnya pada klien." Lebih lanjut, saya berpendapat bahwa, idealnya, otentikasi juga harus tanpa kewarganegaraan di sisi server, yaitu, daripada menyimpan "tiket otentikasi" aktif dalam database, server harus dapat memverifikasi kredensial otentikasi hanya berdasarkan kredensial itu sendiri, misalnya dengan menggunakan token kriptografi mandiri bersama dengan kunci privat. Jadi, alih-alih sumber daya / sesi, seseorang dapat memperkenalkan sumber daya / otentikasi, tetapi itu juga tidak benar-benar menyelesaikan masalah ...
raner
Sebenarnya, / login dan / logout adalah kata benda. Saya berasumsi bahwa Anda sedang memikirkan / log_in dan / log_out.
TiggerToo
21

Saya hanya akan berbicara dari pengalaman saya mengintegrasikan berbagai Layanan Web REST untuk klien saya, apakah itu digunakan untuk aplikasi seluler atau untuk komunikasi server ke server serta membangun REST API untuk orang lain. Berikut adalah beberapa observasi yang telah saya kumpulkan dari REST API orang lain serta yang kami buat sendiri:

  • Ketika kita mengatakan API, biasanya mengacu pada set antarmuka pemrograman dan tidak perlu lapisan presentasi. REST juga berpusat pada data dan tidak didorong oleh presentasi. Artinya, sebagian besar REST mengembalikan data dalam bentuk JSON atau XML dan jarang menampilkan lapisan presentasi tertentu. Sifat ini (mengembalikan data dan bukan halaman web langsung) memberikan kemampuan REST untuk melakukan pengiriman multi-saluran. Artinya, layanan web yang sama dapat dirender dalam HTML, iOS, Android, atau bahkan digunakan sebagai kombinasi server ke server.
  • Sangat jarang untuk menggabungkan HTML dan REST sebagai URL. Secara default REST adalah pemikiran sebagai layanan dan tidak memiliki lapisan presentasi. Ini adalah tugas mereka yang menggunakan layanan web untuk merender data dari layanan yang mereka panggil sesuai dengan apa yang mereka inginkan. Sampai saat itu, URL Anda di bawah ini tidak sesuai dengan sebagian besar desain berbasis REST yang saya temui sejauh ini (maupun standar seperti yang berasal dari Facebook atau Twitter)
GET / register // mendapatkan halaman web yang memiliki formulir pendaftaran
  • Melanjutkan dari poin sebelumnya, juga jarang (dan saya belum menemui) untuk layanan berbasis REST untuk melakukan pengalihan seperti yang disarankan di bawah ini:
GET / logout // hancurkan sesi dan alihkan ke /
POST / login // mengautentikasi kredensial terhadap database dan mengalihkan ke rumah dengan sesi baru atau dialihkan kembali ke / login
 

Karena REST dirancang sebagai layanan, fungsi seperti login dan logout biasanya mengembalikan hasil keberhasilan / kegagalan (biasanya dalam format data JSON atau XML) yang kemudian akan ditafsirkan oleh konsumen. Interpretasi tersebut dapat mencakup pengalihan ke halaman web yang sesuai seperti yang Anda sebutkan

  • Di REST, URL menandakan tindakan yang diambil. Oleh karena itu, kita harus menghilangkan ambiguitas sebanyak mungkin. Meskipun sah dalam kasus Anda untuk memiliki GET dan POST yang memiliki jalur yang sama (seperti / register) yang melakukan tindakan berbeda, desain seperti itu menimbulkan ambiguitas dalam layanan yang disediakan dan dapat membingungkan konsumen layanan Anda. Misalnya, URL seperti yang Anda perkenalkan di bawah ini tidak ideal untuk layanan berbasis REST
GET / register // mendapatkan halaman web yang memiliki formulir pendaftaran
POST / register // mencatat informasi yang dimasukkan ke dalam database sebagai / pengguna / xxx baru

Itulah beberapa poin dari apa yang telah saya tangani. Saya harap ini bisa memberikan beberapa wawasan untuk Anda.

Sekarang sejauh implementasi REST Anda, ini adalah implementasi khas yang saya temui:

  • GET / logout  
    

    Jalankan logout di backend dan kembalikan JSON untuk menunjukkan keberhasilan / kegagalan operasi

  • POST / login
    

    Kirimkan kredensial ke backend. Kembalikan kesuksesan / kegagalan. Jika berhasil, biasanya itu juga akan mengembalikan token sesi serta informasi profil.

  • POST / register
    

    Kirimkan pendaftaran ke backend. Kembalikan kesuksesan / kegagalan. Jika berhasil, biasanya diperlakukan sama dengan login yang berhasil atau Anda dapat memilih untuk membuat pendaftaran sebagai layanan yang berbeda

  • DAPATKAN / pengguna / xxx
    

    Dapatkan profil pengguna dan kembalikan format data JSON untuk profil pengguna

  • POST / pengguna / xxx 
    // diganti namanya menjadi 
    POST / updateUser / xxx
    

    Posting informasi profil yang diperbarui sebagai format JSON dan perbarui informasi di backend. Kembalikan berhasil / gagal ke pemanggil

momo
sumber
3
Ya, jika Anda mengintegrasikan REST API Anda dengan aplikasi berbasis HTML (melalui Javascript dan AJAX), Anda akan mendapatkan keuntungan luar biasa karena JSON diurai secara native oleh Javascript. Di Android / Java, JSON juga lebih mudah dan lebih mudah untuk diurai dibandingkan dengan XML.
momo
15
GET / logout berbahaya. GET harus idempoten. Juga browser ingin mengambil <a> hrefs, yang akan mengeluarkan Anda!
Kugel
4

Saya percaya ini adalah pendekatan yang tenang untuk otentikasi. Untuk Login yang Anda gunakan HttpPut. Metode HTTP ini dapat digunakan untuk pembuatan jika kunci disediakan, dan panggilan berulang tidak berdaya. Untuk LogOff, Anda menentukan jalur yang sama di bawah HttpDeletemetode. Tidak ada kata kerja yang digunakan. Pluralisasi koleksi yang tepat. Metode HTTP mendukung tujuan tersebut.

[HttpPut]
[Route("sessions/current")]
public IActionResult LogIn(LogInModel model) { ... }

[HttpDelete]
[Route("sessions/current")]
public IActionResult LogOff() { ... }

Jika diinginkan, Anda dapat mengganti arus dengan aktif.

Chad Kuehn
sumber
1

Saya akan merekomendasikan menggunakan URL akun pengguna yang mirip dengan twitter di mana URL akun pengguna akan menjadi seperti foo.com/myUserNameseperti Anda bisa masuk ke akun twitter saya dengan URL https://twitter.com/joelbyler

Saya tidak setuju tentang logout yang membutuhkan POST. Sebagai bagian dari API Anda, jika Anda akan mempertahankan sesi, maka id sesi dalam bentuk UUID mungkin merupakan sesuatu yang dapat digunakan untuk melacak pengguna dan mengonfirmasi bahwa tindakan yang diambil telah diotorisasi. Kemudian, bahkan GET dapat meneruskan id sesi ke sumber daya.

Singkatnya saya akan merekomendasikan agar Anda tetap sederhana, URL harus pendek dan mudah diingat.

joelbyler
sumber
Pertanyaannya adalah tentang sumber daya API. Jawaban Anda adalah tentang lapisan presentasi.
Henno