KHẮC PHỤC thiết kế / đăng nhập hoặc / đăng ký tài nguyên?


122

Tôi đang thiết kế một ứng dụng web và sau đó dừng lại để suy nghĩ về cách api của tôi nên được thiết kế như một dịch vụ web RESTful. Hiện tại, hầu hết các URI của tôi là chung và có thể áp dụng cho các ứng dụng web khác nhau:

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

Tôi có cảm giác rằng tôi đang làm rất nhiều sai ở đây sau khi xem xét trên SO và google.

Bắt đầu với /logout, có lẽ vì tôi không thực sự GETbất cứ điều gì - nó có thể thích hợp hơn với POSTyêu cầu /logouthủy phiên và sau đó GETchuyển hướng. Và /logoutkỳ hạn có nên ở lại không?

Còn về /login/register. Tôi có thể thay đổi /registerthành /registrationnhưng điều đó không thay đổi cách dịch vụ của tôi về cơ bản hoạt động - nếu nó có vấn đề sâu hơn.

Bây giờ tôi nhận thấy rằng tôi không bao giờ để lộ /usertài nguyên. Có lẽ điều đó có thể được sử dụng bằng cách nào đó. Ví dụ: lấy người dùng myUser:

foo.com/user/myUser

hoặc là

foo.com/user

Người dùng cuối không yêu cầu thêm chi tiết đó trong URI. Tuy nhiên, cái nào hấp dẫn hơn về mặt trực quan?

Tôi nhận thấy một số câu hỏi khác ở đây trên SO về công việc kinh doanh REST này, nhưng tôi thực sự sẽ đánh giá cao một số hướng dẫn về những gì tôi đã đưa ra ở đây nếu có thể.

Cảm ơn!

CẬP NHẬT:

Tôi cũng muốn có một số ý kiến ​​về:

/user/1

vs

/user/myUserName

Câu trả lời:


63

Một điều đặc biệt nổi lên không phải là REST-ful: việc sử dụng yêu cầu GET để đăng xuất.

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

Một số phương pháp (ví dụ: HEAD, GET, OPTIONS và TRACE) được định nghĩa là an toàn, có nghĩa là chúng chỉ nhằm mục đích truy xuất thông tin và không được thay đổi trạng thái của máy chủ. Nói cách khác, chúng không được có tác dụng phụ, ngoài các tác dụng tương đối vô hại như ghi nhật ký, lưu vào bộ nhớ đệm, phục vụ các quảng cáo biểu ngữ hoặc tăng số lượng truy cập web. [...]

[... H] andling [yêu cầu GET] của máy chủ không bị giới hạn về mặt kỹ thuật theo bất kỳ cách nào. Do đó, lập trình bất cẩn hoặc cố ý có thể gây ra những thay đổi không nhỏ trên máy chủ. Điều này không được khuyến khích, vì nó có thể gây ra sự cố cho bộ nhớ đệm Web, công cụ tìm kiếm và các tác nhân tự động khác […]

Đối với đăng xuất và chuyển hướng, bạn có thể có một bài đăng lên URI đăng xuất của mình, đưa ra phản hồi 303 chuyển hướng đến trang sau đăng xuất.

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

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

Chỉnh sửa để giải quyết các mối quan tâm về thiết kế URL:

"Làm cách nào để thiết kế tài nguyên của tôi?" là một câu hỏi quan trọng đối với tôi; "làm cách nào để thiết kế URL của tôi?" là một sự cân nhắc trong hai lĩnh vực:

URL mà người dùng sẽ thấy không được quá xấu và có ý nghĩa nếu có thể; nếu bạn muốn cookie được gửi trong các yêu cầu đến một số tài nguyên mà không phải tài nguyên khác, bạn sẽ muốn cấu trúc đường dẫn và đường dẫn cookie của mình.

Nếu JRandomUsermuốn xem tiểu sử của chính anh ấy và bạn muốn URL đẹp hơn foo.com/user/JRandomUserhoặc foo.com/user/(JRandom's numeric user id here), bạn có thể tạo một URL riêng chỉ để người dùng xem thông tin của chính họ:

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.
                     */

Tôi có thể khẳng định sự thiếu hiểu biết dễ dàng hơn nhiều so với sự khôn ngoan về chủ đề này, nhưng đây là một số cân nhắc về thiết kế tài nguyên:

  1. Người tiêu dùng: những tài nguyên nào được dùng để xem trực tiếp trong trình duyệt, tải qua XHR hoặc được truy cập bởi một số loại khách hàng khác?
  2. Truy cập / nhận dạng: phản hồi có phụ thuộc vào cookie hoặc liên kết giới thiệu không?

1
Câu trả lời tuyệt vời, cảm ơn! Nếu tôi định triển khai đề xuất URL riêng của bạn ( GET foo.com/profile/) thì đó có phải là một phần của lớp trình bày như momo đã đề xuất không? Nói cách khác, chính xác thì GETyêu cầu đó nên trả về là gì? Một trang web hay một số JSON?
Qcom

2
Ah, tôi nghĩ là tôi đã thấy. Câu trả lời của Momo thực sự làm sáng tỏ mọi thứ. Vì vậy, một API RESTful được xây dựng để cho phép nhiều nền tảng để GET, POST, PUT, và DELETEnguồn lực. Một trang web chỉ là một nền tảng khác truy cập API. Nói cách khác, thiết kế URL trang web hoàn toàn khác với thiết kế RESTful API. Xin hãy cho tôi biết nếu tôi vẫn còn sai haha.
Qcom

Có, hãy đặt REST API của bạn thành một bộ URL và trang web của bạn là một bộ khác. Sau đó, URL trang web của bạn sẽ cung cấp cho bạn HTML + Javascript thích hợp để trang sẽ tạo các XmlHttpRequests thích hợp cho các URL API của bạn để hoạt động như một ứng dụng khách.
ellisbben

129

RESTful có thể được sử dụng như một hướng dẫn để xây dựng URL và bạn có thể tạo phiên và tài nguyên người dùng :

  • GET /session/new lấy trang web có biểu mẫu đăng nhập
  • POST /session xác thực thông tin xác thực dựa trên cơ sở dữ liệu
  • DELETE /session hủy phiên và chuyển hướng đến /
  • GET /users/new lấy trang web có biểu mẫu đăng ký
  • POST /users ghi lại thông tin đã nhập vào cơ sở dữ liệu dưới dạng mới / người dùng / xxx
  • GET /users/xxx // lấy và hiển thị dữ liệu người dùng hiện tại trong chế độ xem hồ sơ
  • POST /users/xxx // cập nhật thông tin mới về người dùng

Chúng có thể là số nhiều hoặc số ít (tôi không chắc cái nào đúng). Tôi thường sử dụng /userstrang chỉ mục người dùng (như mong đợi) và /sessionsđể xem ai đã đăng nhập (như mong đợi).

Sử dụng tên trong URL thay vì một số ( /users/43so với /users/joe) thường được thúc đẩy bởi mong muốn thân thiện hơn với người dùng hoặc công cụ tìm kiếm, chứ không phải bất kỳ yêu cầu kỹ thuật nào. Hoặc là tốt, nhưng tôi khuyên bạn nên nhất quán.

Tôi nghĩ nếu bạn sử dụng đăng ký / đăng nhập / đăng xuất hoặc sign(in|up|out), nó không hoạt động tốt với thuật ngữ còn lại.


6
Tuyệt vời! Tôi thích cách bạn sử dụng những nguồn đó; nó khá sạch sẽ. Mặc dù, từ những gì tôi đã nghe, không phải là hấp dẫn /newcho GET /session/người không phải là RESTful? Tôi đã nghe nói rằng các động từ thường để lại cho các động từ HTTP ( GET, POST, vv).
Qcom

2
@Zach mới không phải là một động từ. Trong trường hợp này, đó là một nguồn phụ của phiên.
Kugel

Làm thế nào để xác định phiên cần xóa trong DELETE / phiên? Curl không gửi cookie hay bất kỳ thông số nào trong yêu cầu DELETE. Tôi giả sử - chỉ để sử dụng DELETE / session / sessionId? Một câu hỏi khác là làm thế nào để trả lại id phiên trong POST / phiên và ở định dạng nào.
Tvaroh

9
Nghỉ ngơi thực sự là một cách để khiến bản thân không hài lòng và lãng phí thời gian cho những việc không quan trọng.
Jian Chen

6
Cá nhân tôi không thích ý tưởng có các tuyến đường trả về biểu mẫu (/ mới). Điều này phá vỡ sự tách biệt giữa chế độ xem và logic nghiệp vụ. Tha cho biết, nếu không có / các tuyến đường mới, một tuyến đường được đề xuất trông hoàn hảo.
Scadge

60

Phiên không RESTful

  • Vâng tôi biết. Nó đang được thực hiện, thường là với OAuth, nhưng thực sự các phiên không RESTful. Bạn không nên có tài nguyên / login / logout chủ yếu vì bạn không nên có phiên.

  • Nếu bạn định làm điều đó, hãy KHỞI ĐỘNG. Tài nguyên là danh từ và / đăng nhập và / đăng xuất không phải là danh từ. Tôi sẽ đi với / phiên. Điều này làm cho việc tạo và xóa trở thành một hành động tự nhiên hơn.

  • ĐĂNG so với GET cho các phiên thật dễ dàng. Nếu bạn đang gửi người dùng / mật khẩu dưới dạng biến, tôi sẽ sử dụng POST vì tôi không muốn mật khẩu được gửi như một phần của URI. Nó sẽ hiển thị trong nhật ký và có thể được hiển thị trên dây. Bạn cũng có nguy cơ bị lỗi phần mềm do các giới hạn của GET args.

  • Tôi thường sử dụng Basic Auth hoặc no Auth với các dịch vụ REST.

Tạo người dùng

  • Đó là một tài nguyên, vì vậy bạn không cần / đăng ký.

    • POST / user - Tạo người dùng nếu người yêu cầu không thể chỉ định id
    • PUT / user / xxx - Tạo hoặc cập nhật một người dùng giả sử bạn biết trước id
    • GET / user - danh sách x id người dùng
    • GET / user / xxx - Nhận thông tin chi tiết của người dùng có id xxx
    • DELETE / user / xxx - Xóa người dùng có id xxx
  • Sử dụng loại ID nào là một câu hỏi khó. Bạn phải nghĩ đến việc thực thi tính duy nhất, về việc sử dụng lại các id cũ đã bị DELETEd. Ví dụ: bạn không muốn sử dụng các id đó làm khóa ngoại trên chương trình phụ trợ nếu các id sẽ được tái chế (nếu có thể). Tuy nhiên, bạn có thể tra cứu chuyển đổi id bên ngoài / bên trong để giảm thiểu các yêu cầu phụ trợ.


6
Đây là câu trả lời tốt nhất. / login và / logout không phải là tài nguyên và phá vỡ ý tưởng về REST.
wle8300

5
Authentication! = Session
dietbuddha

1
Đúng vậy, luận điểm của Fielding nói rõ trong phần 5.1.3 rằng "[s] trạng thái ession [...] được giữ hoàn toàn trên máy khách." Hơn nữa, tôi sẽ tranh luận rằng, về mặt lý tưởng, xác thực cũng nên không trạng thái ở phía máy chủ, tức là, thay vì lưu trữ "phiếu xác thực" đang hoạt động trong cơ sở dữ liệu, máy chủ phải có thể xác minh thông tin xác thực chỉ dựa trên chính thông tin đó, ví dụ: bằng cách sử dụng mã thông báo mật mã độc lập kết hợp với khóa riêng. Vì vậy, thay vì tài nguyên / phiên, người ta có thể giới thiệu một tài nguyên / xác thực, nhưng nó cũng không thực sự giải quyết được vấn đề ...
raner

Trên thực tế, / đăng nhập và / đăng xuất là danh từ. Tôi cho rằng bạn đang nghĩ đến / log_in và / log_out.
TiggerToo

21

Tôi sẽ chỉ đơn giản nói từ kinh nghiệm của tôi khi tích hợp các Dịch vụ Web REST khác nhau cho các khách hàng của tôi, cho dù nó được sử dụng cho các ứng dụng di động hay cho giao tiếp máy chủ với máy chủ cũng như xây dựng API REST cho những người khác. Dưới đây là một vài quan sát mà tôi thu thập được từ API REST của người khác cũng như những thứ mà chúng tôi tự xây dựng:

  • Khi chúng ta nói API, nó thường đề cập đến tập hợp các giao diện lập trình và không cần thiết đến lớp trình bày. REST cũng tập trung vào dữ liệu và không hướng đến bản trình bày. Điều đó nói rằng hầu hết dữ liệu trả về REST ở dạng JSON hoặc XML và hiếm khi trả về một lớp trình bày cụ thể. Đặc điểm này (của dữ liệu trả về chứ không phải trang web trực tiếp) cho phép REST thực hiện phân phối đa kênh. Có nghĩa là cùng một dịch vụ web có thể được hiển thị bằng HTML, iOS, Android hoặc thậm chí được sử dụng làm tổ hợp máy chủ với máy chủ.
  • Rất hiếm khi kết hợp cả HTML và REST làm URL. Theo mặc định, REST là những suy nghĩ là dịch vụ và không có lớp trình bày. Công việc của những người sử dụng dịch vụ web là hiển thị dữ liệu từ các dịch vụ mà họ gọi theo những gì họ muốn. Vì vậy, URL của bạn dưới đây không phù hợp với hầu hết thiết kế dựa trên REST mà tôi đã gặp cho đến nay (cũng không phải các tiêu chuẩn như những người đến từ Facebook hoặc Twitter)
GET / register // lấy trang web có biểu mẫu đăng ký
  • Tiếp tục từ điểm trước, cũng không phổ biến (và tôi chưa gặp phải) dịch vụ dựa trên REST thực hiện chuyển hướng như những dịch vụ được đề xuất bên dưới:
GET / logout // hủy phiên và chuyển hướng đến /
POST / login // xác thực thông tin xác thực dựa trên cơ sở dữ liệu và chuyển hướng về nhà với một phiên mới hoặc chuyển hướng trở lại / login
 

Vì REST được thiết kế dưới dạng dịch vụ, chức năng như đăng nhập và đăng xuất thường trả về kết quả thành công / thất bại (thường ở định dạng dữ liệu JSON hoặc XML) mà sau đó người tiêu dùng sẽ diễn giải. Diễn giải như vậy có thể bao gồm việc chuyển hướng đến trang web thích hợp như bạn đã đề cập

  • Trong REST, URL biểu thị các hành động được thực hiện. Vì lý do đó, chúng ta nên loại bỏ càng nhiều sự mơ hồ càng tốt. Mặc dù trong trường hợp của bạn, cả GET và POST có cùng một đường dẫn (chẳng hạn như / đăng ký) thực hiện hành động khác nhau là hợp pháp, nhưng thiết kế như vậy gây ra sự mơ hồ trong các dịch vụ được cung cấp và có thể gây nhầm lẫn cho người tiêu dùng dịch vụ của bạn. Ví dụ: các URL như URL mà bạn giới thiệu bên dưới không lý tưởng cho các dịch vụ dựa trên REST
GET / register // lấy trang web có biểu mẫu đăng ký
POST / register // ghi lại thông tin đã nhập vào cơ sở dữ liệu dưới dạng / user / xxx mới

Đó là một số điểm từ những gì tôi đã xử lý. Tôi hy vọng nó có thể cung cấp một số thông tin chi tiết cho bạn.

Bây giờ cho đến khi triển khai REST của bạn, đây là cách triển khai điển hình mà tôi đã gặp:

  • GET / đăng xuất  
    

    Thực hiện đăng xuất trong phần phụ trợ và trả về JSON để biểu thị sự thành công / thất bại của hoạt động

  • ĐĂNG / đăng nhập
    

    Gửi thông tin đăng nhập cho chương trình phụ trợ. Trả lại thành công / thất bại. Nếu thành công, thông thường nó cũng sẽ trả về mã thông báo phiên cũng như thông tin hồ sơ.

  • ĐĂNG / đăng ký
    

    Gửi đăng ký đến chương trình phụ trợ. Trả lại thành công / thất bại. Nếu thành công, thường được coi như đăng nhập thành công hoặc bạn có thể chọn đăng ký như một dịch vụ riêng biệt

  • NHẬN / người dùng / xxx
    

    Nhận hồ sơ người dùng và trả về định dạng dữ liệu JSON cho hồ sơ của người dùng

  • ĐĂNG / người dùng / xxx 
    // đổi tên thành 
    POST / updateUser / xxx
    

    Đăng thông tin hồ sơ cập nhật ở định dạng JSON và cập nhật thông tin trong chương trình phụ trợ. Trả lại thành công / thất bại cho người gọi


3
Có, nếu bạn đang tích hợp API REST của mình với ứng dụng dựa trên HTML (thông qua Javascript và AJAX), bạn sẽ nhận được lợi ích to lớn vì JSON được phân tích cú pháp nguyên bản bằng Javascript. Trong Android / Java, JSON cũng dễ dàng và đơn giản hơn để phân tích cú pháp so với XML.
momo,

15
GET / đăng xuất rất nguy hiểm. GET phải là giá trị trung tâm. Các trình duyệt cũng thích tìm nạp trước <a> hrefs, điều này sẽ giúp bạn đăng xuất!
Kugel

4

Tôi tin rằng đây là một cách tiếp cận RESTful để xác thực. Đối với Đăng nhập bạn sử dụng HttpPut. Phương thức HTTP này có thể được sử dụng để tạo khi khóa được cung cấp và các cuộc gọi lặp lại là không quan trọng. Đối với LogOff, bạn chỉ định cùng một đường dẫn trong HttpDeletephương thức. Không sử dụng động từ. Đa dạng hóa bộ sưu tập thích hợp. Các phương thức HTTP hỗ trợ mục đích.

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

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

Nếu muốn, bạn có thể thay thế hiện tại cho hoạt động.


1

Tôi khuyên bạn nên sử dụng URL tài khoản người dùng tương tự như twitter trong đó URL tài khoản của người dùng sẽ giống foo.com/myUserNamenhư bạn có thể truy cập tài khoản twitter của tôi bằng URL https://twitter.com/joelbyler

Tôi không đồng ý về việc đăng xuất yêu cầu ĐĂNG. Là một phần của API của bạn, nếu bạn định duy trì một phiên, thì id phiên ở dạng UUID có thể là thứ có thể được sử dụng để theo dõi người dùng và xác nhận rằng hành động đang được thực hiện được ủy quyền. Sau đó, ngay cả một GET cũng có thể chuyển cùng id phiên tới tài nguyên.

Tóm lại, tôi khuyên bạn nên giữ nó đơn giản, URL phải ngắn gọn và dễ nhớ.


Câu hỏi là về tài nguyên API. Câu trả lời của bạn là về lớp trình bày.
Henno
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.