Hiểu REST: Động từ, mã lỗi và xác thực


602

Tôi đang tìm cách bọc API xung quanh các chức năng mặc định trong các ứng dụng web, cơ sở dữ liệu và CMS dựa trên PHP của tôi.

Tôi đã nhìn xung quanh và tìm thấy một số khung "bộ xương". Ngoài các câu trả lời trong câu hỏi của tôi, còn có Tonic , một khung REST tôi thích vì nó rất nhẹ.

Tôi thích REST vì sự đơn giản của nó và muốn tạo một kiến ​​trúc API dựa trên nó. Tôi đang cố gắng tìm hiểu các nguyên tắc cơ bản và chưa hiểu đầy đủ về nó. Do đó, một số câu hỏi.

1. Tôi có hiểu đúng không?

Nói rằng tôi có một tài nguyên "người dùng". Tôi có thể thiết lập một số URI như vậy:

/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

đây có phải là một đại diện chính xác của kiến ​​trúc RESTful cho đến nay?

2. Tôi cần thêm động từ

Tạo, cập nhật và xóa có thể là đủ trong lý thuyết, nhưng trong thực tế tôi sẽ có nhu cầu nhiều động từ hơn. Tôi nhận ra đây là những thứ có thể được nhúng trong yêu cầu cập nhật, nhưng chúng là những hành động cụ thể có thể có mã trả lại cụ thể và tôi không muốn ném tất cả chúng vào một hành động.

Một số điều mà tôi nghĩ đến trong ví dụ người dùng là:

activate_login
deactivate_login
change_password
add_credit

Làm cách nào để thể hiện các hành động như những hành động trong kiến ​​trúc URL RESTful?

Bản năng của tôi sẽ là thực hiện một cuộc gọi NHẬN tới một URL như

/api/users/1/activate_login 

và mong đợi một mã trạng thái trở lại.

Tuy nhiên, điều đó đi lệch khỏi ý tưởng sử dụng động từ HTTP. Bạn nghĩ sao?

3. Cách trả về thông báo lỗi và mã

Một phần lớn vẻ đẹp của REST bắt nguồn từ việc sử dụng các phương thức HTTP tiêu chuẩn. Khi xảy ra lỗi, tôi phát ra một tiêu đề với mã trạng thái lỗi 3xx, 4xx hoặc 5xx. Đối với một mô tả lỗi chi tiết, tôi có thể sử dụng cơ thể (phải không?). Càng xa càng tốt. Nhưng điều gì sẽ là cách truyền mã lỗi độc quyền chi tiết hơn trong việc mô tả những gì đã sai (ví dụ: "không thể kết nối với cơ sở dữ liệu" hoặc "đăng nhập cơ sở dữ liệu sai")? Nếu tôi đặt nó vào cơ thể cùng với tin nhắn, tôi phải phân tích nó ra sau đó. Có một tiêu đề tiêu chuẩn cho loại điều này?

4. Cách xác thực

  • Xác thực dựa trên khóa API theo nguyên tắc REST sẽ như thế nào?
  • Có những điểm mạnh chống lại việc sử dụng các phiên khi xác thực ứng dụng khách REST, ngoài việc đó có phải là vi phạm trắng trợn nguyên tắc REST không? :) (chỉ đùa một nửa ở đây, xác thực dựa trên phiên sẽ chơi tốt với cơ sở hạ tầng hiện có của tôi.)

13
@Daniel, cảm ơn đã chỉnh sửa. "Tôi nhiều động từ hơn" là một cách chơi chữ có chủ ý, nhưng tôi để nó như hiện tại, nó dễ đọc hơn bây giờ. :)
Pekka

1
BTW, về mô tả lỗi. Tôi đã kết thúc với việc đưa mô tả lỗi vào tiêu đề phản hồi. Chỉ cần thêm tiêu đề có tên 'Mô tả lỗi'.
Andrii Muzychuk 10/03/2015

Điều này trông giống như câu hỏi bảo mật ứng dụng. Bảo mật ứng dụng không phải là những gì REST hướng tới.
Nazar Merza

@NazarMerza là 1., 2. và 3. câu hỏi bảo mật ứng dụng như thế nào?
Pekka

Câu trả lời:


621

Tôi nhận thấy câu hỏi này muộn vài ngày, nhưng tôi cảm thấy rằng tôi có thể thêm một số hiểu biết. Tôi hy vọng điều này có thể hữu ích đối với liên doanh RESTful của bạn.


Điểm 1: Tôi có hiểu đúng không?

Bạn đã hiểu đúng. Đó là một đại diện chính xác của một kiến ​​trúc RESTful. Bạn có thể thấy ma trận sau từ Wikipedia rất hữu ích trong việc xác định danh từ và động từ của bạn:


Khi giao dịch với URI Bộ sưu tập như:http://example.com/resources/

  • NHẬN : Liệt kê các thành viên của bộ sưu tập, hoàn thành với các URI thành viên của họ để điều hướng thêm. Ví dụ, liệt kê tất cả những chiếc xe để bán.

  • PUT : Có nghĩa là "thay thế toàn bộ bộ sưu tập bằng bộ sưu tập khác".

  • POST : Tạo một mục mới trong bộ sưu tập trong đó ID được gán tự động bởi bộ sưu tập. ID được tạo thường được bao gồm như một phần của dữ liệu được trả về bởi thao tác này.

  • XÓA : Có nghĩa là "xóa toàn bộ bộ sưu tập".


Khi giao dịch với URI thành viên như:http://example.com/resources/7HOU57Y

  • NHẬN : Truy xuất một đại diện của thành viên được đánh địa chỉ của bộ sưu tập được thể hiện bằng loại MIME thích hợp.

  • PUT : Cập nhật thành viên địa chỉ của bộ sưu tập hoặc tạo nó với ID được chỉ định.

  • POST : Xử lý các thành viên được đánh địa chỉ như một bộ sưu tập theo cách riêng của mình và tạo ra một cấp dưới mới của nó.

  • XÓA : Xóa thành viên địa chỉ của bộ sưu tập.


Điểm 2: Tôi cần nhiều động từ hơn

Nói chung, khi bạn nghĩ rằng bạn cần nhiều động từ hơn, điều đó thực sự có nghĩa là tài nguyên của bạn cần được xác định lại. Hãy nhớ rằng trong REST bạn luôn hành động trên một tài nguyên hoặc trên một bộ sưu tập tài nguyên. Những gì bạn chọn làm tài nguyên khá quan trọng đối với định nghĩa API của bạn.

Kích hoạt / Hủy kích hoạt đăng nhập : Nếu bạn đang tạo một phiên mới, thì bạn có thể muốn coi "phiên" là tài nguyên. Để tạo một phiên mới, sử dụng POST http://example.com/sessions/với thông tin đăng nhập trong phần thân. Để hết hạn, hãy sử dụng PUT hoặc XÓA (có thể tùy thuộc vào việc bạn có ý định giữ lịch sử phiên) hay không http://example.com/sessions/SESSION_ID.

Thay đổi mật khẩu: Lần này tài nguyên là "người dùng". Bạn sẽ cần PUT http://example.com/users/USER_IDvới mật khẩu cũ và mới trong cơ thể. Bạn đang hành động trên tài nguyên "người dùng" và mật khẩu thay đổi chỉ đơn giản là một yêu cầu cập nhật. Nó khá giống với câu lệnh CẬP NHẬT trong cơ sở dữ liệu quan hệ.

Bản năng của tôi sẽ là thực hiện một cuộc gọi NHẬN tới một URL như /api/users/1/activate_login

Điều này đi ngược lại một nguyên tắc REST rất cốt lõi: Việc sử dụng đúng các động từ HTTP. Bất kỳ yêu cầu GET không bao giờ nên để lại bất kỳ tác dụng phụ.

Ví dụ: yêu cầu GET không bao giờ tạo phiên trên cơ sở dữ liệu, trả lại cookie với ID phiên mới hoặc để lại bất kỳ dư lượng nào trên máy chủ. Động từ GET giống như câu lệnh SELECT trong công cụ cơ sở dữ liệu. Hãy nhớ rằng phản hồi cho bất kỳ yêu cầu nào với động từ GET phải có khả năng lưu trữ bộ đệm khi được yêu cầu có cùng tham số, giống như khi bạn yêu cầu một trang web tĩnh.


Điểm 3: Cách trả về thông báo lỗi và mã

Xem xét mã trạng thái HTTP 4xx hoặc 5xx làm danh mục lỗi. Bạn có thể xây dựng các lỗi trong cơ thể.

Không thể kết nối với cơ sở dữ liệu: / Đăng nhập cơ sở dữ liệu không chính xác : Nói chung bạn nên sử dụng lỗi 500 cho các loại lỗi này. Đây là một lỗi phía máy chủ. Khách hàng không làm gì sai. 500 lỗi thường được coi là "có thể thử lại". tức là khách hàng có thể thử lại cùng một yêu cầu chính xác và hy vọng nó sẽ thành công sau khi các rắc rối của máy chủ được giải quyết. Chỉ định các chi tiết trong cơ thể, để khách hàng có thể cung cấp một số bối cảnh cho con người chúng ta.

Các loại lỗi khác sẽ là họ 4xx, nói chung chỉ ra rằng máy khách đã làm gì đó sai. Cụ thể, loại lỗi này thường cho khách hàng biết rằng không cần phải thử lại yêu cầu, vì nó sẽ tiếp tục bị lỗi vĩnh viễn. tức là khách hàng cần thay đổi một cái gì đó trước khi thử lại yêu cầu này. Ví dụ: lỗi "Không tìm thấy tài nguyên" (HTTP 404) hoặc "Yêu cầu không đúng định dạng" (HTTP 400) sẽ thuộc loại này.


Điểm 4: Cách xác thực

Như đã chỉ ra ở điểm 1, thay vì xác thực người dùng, bạn có thể muốn nghĩ về việc tạo phiên. Bạn sẽ được trả lại "ID phiên" mới, cùng với mã trạng thái HTTP thích hợp (200: Quyền truy cập được cấp hoặc 403: Từ chối truy cập).

Sau đó, bạn sẽ hỏi máy chủ RESTful của mình: "Bạn có thể lấy cho tôi tài nguyên cho ID phiên này không?".

Không có chế độ xác thực - REST không trạng thái: Bạn tạo phiên, bạn yêu cầu máy chủ cung cấp cho bạn tài nguyên bằng ID phiên này làm tham số và khi đăng xuất bạn bỏ hoặc hết hạn phiên.


6
Rất tốt, tuy nhiên việc bạn sử dụng PUTđể thay đổi mật khẩu có thể không chính xác; PUTyêu cầu toàn bộ tài nguyên, do đó bạn phải gửi tất cả các thuộc tính người dùng để tuân thủ HTTP (và do đó với HATEOAS REST). Thay vào đó, để đơn giản thay đổi mật khẩu người ta nên sử dụng PATCHhoặc POST.
Lawrence Dol

1
Tôi nghĩ rằng bài đăng này sẽ hoàn hảo nếu bạn mở rộng thêm về nội dung "POST: Xử lý thành viên được đánh địa chỉ như một bộ sưu tập theo cách riêng của mình và tạo ra một cấp dưới mới của nó." có nghĩa. - Tôi đã tìm thấy ý nghĩa của Googling - đó là một ngoại lệ đối với câu trả lời tuyệt vời khác của bạn.
Martin Konecny

6
Tôi không đồng ý với câu cuối cùng. Bạn đang giải thích làm thế nào REST là không quốc tịch. Đăng nhập để tạo phiên, sau đó đăng xuất để kết thúc phiên sau khi thực hiện một số công việc là ví dụ tốt nhất về API trạng thái.
Brandon

1
"Điều này đi ngược lại một nguyên tắc REST rất cốt lõi: Việc sử dụng chính xác các động từ HTTP. Mọi yêu cầu GET không bao giờ được để lại bất kỳ tác dụng phụ nào." - Điều gì sẽ xảy ra nếu bạn muốn duy trì số lần truy cập cho tài nguyên?
bulkalex

1
Bài viết này sẽ trả lời câu hỏi của bạn. saipraveenblog.wordpress.com/2014/09/29/rest-api-best-practices
java_geek

79

Nói một cách đơn giản, bạn đang làm điều này hoàn toàn lạc hậu.

Bạn không nên tiếp cận điều này từ những URL bạn nên sử dụng. Các URL sẽ thực sự "miễn phí" một khi bạn đã quyết định tài nguyên nào là cần thiết cho hệ thống của mình VÀ cách bạn sẽ đại diện cho các tài nguyên đó và các tương tác giữa tài nguyên và trạng thái ứng dụng.

Để trích dẫn Roy Fielding

API REST nên dành hầu hết tất cả nỗ lực mô tả của nó để xác định (các) loại phương tiện được sử dụng để thể hiện tài nguyên và trạng thái ứng dụng lái xe hoặc trong việc xác định tên quan hệ mở rộng và / hoặc đánh dấu siêu văn bản cho các loại phương tiện tiêu chuẩn hiện có. Bất kỳ nỗ lực nào dành cho việc mô tả các phương pháp sử dụng cho URI quan tâm nào đều phải được xác định hoàn toàn trong phạm vi quy tắc xử lý cho loại phương tiện (và, trong hầu hết các trường hợp, đã được xác định bởi các loại phương tiện hiện có). [Thất bại ở đây ngụ ý rằng thông tin ngoài băng tần đang thúc đẩy sự tương tác thay vì siêu văn bản.]

Mọi người luôn bắt đầu với các URI và nghĩ rằng đây là giải pháp và sau đó họ có xu hướng bỏ lỡ một khái niệm quan trọng trong kiến ​​trúc REST, đáng chú ý, như được trích dẫn ở trên, "Thất bại ở đây ngụ ý rằng thông tin ngoài băng là điều khiển tương tác thay vì siêu văn bản. "

Thành thật mà nói, nhiều người nhìn thấy một loạt các URI và một số GET và PUT và POST và nghĩ rằng REST là dễ dàng. REST không dễ. RPC qua HTTP rất dễ dàng, việc di chuyển các đốm dữ liệu qua lại được ủy quyền thông qua tải trọng HTTP rất dễ dàng. REST, tuy nhiên, vượt xa điều đó. REST là giao thức bất khả tri. HTTP chỉ rất phổ biến và thích hợp cho các hệ thống REST.

REST sống trong các loại phương tiện, định nghĩa của chúng và cách ứng dụng điều khiển các hành động có sẵn cho các tài nguyên đó thông qua siêu văn bản (liên kết, một cách hiệu quả).

Có nhiều cách nhìn khác nhau về các loại phương tiện trong các hệ thống REST. Một số ủng hộ tải trọng cụ thể của ứng dụng, trong khi những người khác thích nâng cao các loại phương tiện hiện có vào các vai trò phù hợp với ứng dụng. Ví dụ, một mặt bạn có các lược đồ XML cụ thể được thiết kế phù hợp với ứng dụng của bạn so với sử dụng thứ gì đó như XHTML làm đại diện của bạn, có lẽ thông qua các vi định dạng và các cơ chế khác.

Cả hai cách tiếp cận đều có vị trí của chúng, tôi nghĩ, XHTML hoạt động rất tốt trong các tình huống chồng lấp cả web điều khiển bằng máy và điều khiển máy, trong khi các loại dữ liệu trước đây, cụ thể hơn tôi cảm thấy tốt hơn khi máy tương tác với máy. Tôi thấy việc nâng cao các định dạng hàng hóa có thể khiến việc đàm phán nội dung trở nên khó khăn. "application / xml + yourresource" là một loại phương tiện cụ thể hơn nhiều so với "application / xhtml + xml", vì cái sau có thể áp dụng cho nhiều tải trọng có thể hoặc không phải là thứ mà máy khách thực sự quan tâm, cũng không thể xác định mà không cần hướng nội.

Tuy nhiên, XHTML hoạt động rất tốt (rõ ràng) trong web của con người nơi trình duyệt web và kết xuất đồ họa rất quan trọng.

Ứng dụng của bạn sẽ hướng dẫn bạn trong những loại quyết định.

Một phần của quá trình thiết kế hệ thống REST là khám phá các tài nguyên hạng nhất trong hệ thống của bạn, cùng với các tài nguyên hỗ trợ phái sinh, cần thiết để hỗ trợ các hoạt động trên các tài nguyên chính. Khi các tài nguyên được phát hiện, thì việc biểu diễn các tài nguyên đó, cũng như các sơ đồ trạng thái hiển thị luồng tài nguyên qua siêu văn bản trong các biểu diễn vì thách thức tiếp theo.

Hãy nhớ lại rằng mỗi đại diện của một tài nguyên, trong một hệ thống siêu văn bản, kết hợp cả biểu diễn tài nguyên thực tế cùng với các chuyển đổi trạng thái có sẵn cho tài nguyên. Xem xét mỗi tài nguyên một nút trong biểu đồ, với các liên kết là các đường rời nút đó sang các trạng thái khác. Các liên kết này thông báo cho khách hàng không chỉ những gì có thể được thực hiện, mà cả những gì cần thiết cho họ phải được thực hiện (vì một liên kết tốt kết hợp URI và loại phương tiện cần thiết).

Ví dụ: bạn có thể có:

<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"/>

Tài liệu của bạn sẽ nói về trường rel có tên "người dùng" và loại phương tiện "application / xml + youruser".

Các liên kết này có vẻ dư thừa, tất cả chúng đều nói về cùng một URI, khá nhiều. Nhưng họ không phải.

Điều này là do đối với mối quan hệ "người dùng", liên kết đó đang nói về bộ sưu tập người dùng và bạn có thể sử dụng giao diện thống nhất để làm việc với bộ sưu tập (GET để lấy tất cả chúng, XÓA để xóa tất cả chúng, v.v.)

Nếu bạn POST vào URL này, bạn sẽ cần chuyển tài liệu "application / xml + usercollection", có thể sẽ chỉ chứa một cá thể người dùng trong tài liệu để bạn có thể thêm người dùng, hoặc có thể, để thêm một số tại Một lần. Có lẽ tài liệu của bạn sẽ gợi ý rằng bạn chỉ cần vượt qua một loại người dùng, thay vì bộ sưu tập.

Bạn có thể xem những gì ứng dụng yêu cầu để thực hiện tìm kiếm, như được xác định bởi liên kết "tìm kiếm" và đó là mediatype. Tài liệu về loại phương tiện tìm kiếm sẽ cho bạn biết cách thức này hoạt động và những gì được mong đợi là kết quả.

Mặc dù vậy, điều đáng nói ở đây là bản thân các URI không quan trọng. Ứng dụng này kiểm soát các URI, không phải máy khách. Ngoài một vài 'điểm vào', khách hàng của bạn nên dựa vào các URI do ứng dụng cung cấp cho công việc của mình.

Khách hàng cần biết cách thao tác và giải thích các loại phương tiện, nhưng không cần quan tâm nhiều đến việc nó đi đâu.

Hai liên kết này giống hệt nhau về mặt ngữ nghĩa trong mắt khách hàng:

<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"/>

Vì vậy, tập trung vào tài nguyên của bạn. Tập trung vào chuyển đổi trạng thái của họ trong ứng dụng và cách đạt được điều đó tốt nhất.


1
Cảm ơn Will cho câu trả lời rất sâu sắc này. Một số điểm thực hiện. Tôi nhận ra rằng việc lập kế hoạch từ "URL trông như thế nào" đang thực hiện theo cách khác và tôi cũng đang lên kế hoạch từ phía tài nguyên. Có URL để chơi chỉ giúp tôi dễ hiểu khái niệm hơn. Có thể là các yêu cầu của tôi có thể được đáp ứng với một hệ thống không tuân thủ 100% các nguyên tắc REST như bạn định nghĩa ở đây. Tôi sẽ rút ra một danh sách đầy đủ các yêu cầu cho từng loại tài nguyên, tôi đoán tôi sẽ có thể quyết định sau đó. Chúc mừng.
Pekka

30

re 1 : Điều này có vẻ tốt cho đến nay. Hãy nhớ trả về URI của người dùng mới được tạo trong tiêu đề "Vị trí:" như một phần của phản hồi với POST, cùng với mã trạng thái "201 được tạo".

lại 2: Kích hoạt thông qua GET là một ý tưởng tồi và bao gồm động từ trong URI là một mùi thiết kế. Bạn có thể muốn xem xét trả lại một biểu mẫu trên GET. Trong một ứng dụng Web, đây sẽ là một hình thức HTML với nút gửi; trong trường hợp sử dụng API, bạn có thể muốn trả về một đại diện có chứa URI thành PUT để kích hoạt tài khoản. Tất nhiên, bạn cũng có thể đưa URI này vào phản hồi trên POST cho / người dùng. Sử dụng PUT sẽ đảm bảo yêu cầu của bạn là bình thường, tức là nó có thể được gửi lại một cách an toàn nếu khách hàng không chắc chắn về thành công. Nói chung, hãy suy nghĩ về những tài nguyên bạn có thể biến động từ của bạn thành (loại "danh từ của động từ"). Tự hỏi phương pháp hành động cụ thể của bạn được liên kết chặt chẽ nhất với. Ví dụ: Change_password -> PUT; hủy kích hoạt -> có thể XÓA; add_credit -> có thể POST hoặc PUT.

lại 3. Không phát minh ra mã trạng thái mới, trừ khi bạn tin rằng chúng quá chung chung, chúng xứng đáng được tiêu chuẩn hóa trên toàn cầu. Cố gắng sử dụng mã trạng thái phù hợp nhất có sẵn (đọc về tất cả chúng trong RFC 2616). Bao gồm thông tin bổ sung trong cơ thể phản ứng. Nếu bạn thực sự, thực sự chắc chắn rằng bạn muốn phát minh ra một mã trạng thái mới, hãy suy nghĩ lại; nếu bạn vẫn tin như vậy, hãy đảm bảo ít nhất chọn đúng danh mục (1xx -> OK, 2xx -> thông tin, 3xx -> chuyển hướng; 4xx-> lỗi máy khách, 5xx -> lỗi máy chủ). Tôi đã đề cập rằng phát minh ra mã trạng thái mới là một ý tưởng tồi?

re 4. Nếu bằng mọi cách có thể, hãy sử dụng khung xác thực được tích hợp trong HTTP. Kiểm tra cách Google xác thực trong GData. Nói chung, không đặt các khóa API trong URI của bạn. Cố gắng tránh các phiên để tăng cường khả năng mở rộng và hỗ trợ bộ nhớ đệm - nếu phản hồi cho yêu cầu khác nhau do điều gì đó đã xảy ra trước đó, bạn thường tự ràng buộc mình với một ví dụ quy trình máy chủ cụ thể. Sẽ tốt hơn nhiều khi biến trạng thái phiên thành trạng thái máy khách (ví dụ: biến nó thành một phần của các yêu cầu tiếp theo) hoặc làm cho nó rõ ràng bằng cách biến nó thành trạng thái tài nguyên (máy chủ), tức là cung cấp URI của chính nó.


Bạn có thể thảo luận tại sao không đặt các khóa API trong URL không? Có phải vì chúng hiển thị trong nhật ký proxy? Điều gì nếu các phím là thoáng qua, dựa trên thời gian? Nếu HTTPS được sử dụng thì sao?
MikeSchinkel

4
Ngoài việc vi phạm tinh thần (URI nên xác định mọi thứ), hậu quả chính là nó phá hỏng bộ nhớ đệm.
Stefan Tilkov

22

1. Bạn đã có ý tưởng đúng về cách thiết kế tài nguyên của mình, IMHO. Tôi sẽ không thay đổi một điều.

2. Thay vì cố gắng mở rộng HTTP với nhiều động từ hơn, hãy xem xét những động từ được đề xuất của bạn có thể được giảm xuống theo các phương thức và tài nguyên HTTP cơ bản. Ví dụ: thay vì một activate_loginđộng từ, bạn có thể thiết lập các tài nguyên như: /api/users/1/login/activeđó là một boolean đơn giản. Để kích hoạt đăng nhập, chỉ cần PUTmột tài liệu có nội dung 'đúng' hoặc 1 hoặc bất cứ điều gì. Để hủy kích hoạt, PUTmột tài liệu ở đó trống hoặc nói 0 hoặc sai.

Tương tự, để thay đổi hoặc đặt mật khẩu, chỉ cần làm PUTs /api/users/1/password.

Bất cứ khi nào bạn cần thêm một cái gì đó (như tín dụng) hãy nghĩ về POSTs. Ví dụ: bạn có thể thực hiện một POSTtài nguyên như /api/users/1/creditsvới phần thân chứa số tín dụng cần thêm. A PUTtrên cùng một tài nguyên có thể được sử dụng để ghi đè giá trị thay vì thêm. Một POSTsố có số âm trong cơ thể sẽ trừ đi, v.v.

3. Tôi thực sự khuyên bạn không nên mở rộng mã trạng thái HTTP cơ bản. Nếu bạn không thể tìm thấy chính xác phù hợp với tình huống của mình, hãy chọn trường hợp gần nhất và đặt chi tiết lỗi trong phần phản hồi. Ngoài ra, hãy nhớ rằng các tiêu đề HTTP có thể mở rộng; ứng dụng của bạn có thể xác định tất cả các tiêu đề tùy chỉnh mà bạn thích. Một ứng dụng mà tôi đã làm việc, ví dụ, có thể trả về 404 Not Foundtrong nhiều trường hợp. Thay vì làm cho máy khách phân tích cú pháp phản hồi vì lý do, chúng tôi chỉ cần thêm một tiêu đề mới X-Status-Extended, trong đó có phần mở rộng mã trạng thái độc quyền của chúng tôi. Vì vậy, bạn có thể thấy một phản hồi như:

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

Theo cách đó, máy khách HTTP như trình duyệt web sẽ vẫn biết phải làm gì với mã 404 thông thường và máy khách HTTP tinh vi hơn có thể chọn xem X-Status-Extendedtiêu đề để biết thông tin cụ thể hơn.

4. Để xác thực, tôi khuyên bạn nên sử dụng xác thực HTTP nếu bạn có thể. Nhưng IMHO không có gì sai khi sử dụng xác thực dựa trên cookie nếu điều đó dễ dàng hơn với bạn.


4
Ý tưởng gọn gàng về việc sử dụng tài nguyên "mở rộng" để thực hiện mọi việc với các phần nhỏ hơn của tài nguyên lớn hơn.
womble

1
Cookie có giá trị trong HTTP / REST, nhưng máy chủ không nên lưu trữ cookie dưới dạng trạng thái (vì vậy không phải là phiên). Cookie có thể lưu trữ một giá trị như một HMAC, tuy nhiên, có thể được tháo rời mà không cần tra cứu trạng thái ở nơi khác.
Bruce Alderson

14

Khái niệm cơ bản về REST

REST có một ràng buộc giao diện thống nhất, trong đó tuyên bố rằng máy khách REST phải dựa trên các tiêu chuẩn thay vì các chi tiết cụ thể của ứng dụng của dịch vụ REST thực tế, vì vậy máy khách REST sẽ không bị hỏng bởi những thay đổi nhỏ và có thể sẽ được sử dụng lại.

Vì vậy, có một hợp đồng giữa máy khách REST và dịch vụ REST. Nếu bạn sử dụng HTTP làm giao thức cơ bản, thì các tiêu chuẩn sau đây là một phần của hợp đồng:

  • HTTP 1.1
    • định nghĩa phương thức
    • định nghĩa mã trạng thái
    • tiêu đề kiểm soát bộ đệm
    • chấp nhận và tiêu đề loại nội dung
    • tiêu đề auth
  • IRI ( URI utf8 )
  • cơ thể (chọn một)
    • ứng dụng đã đăng ký loại MIME cụ thể, ví dụ như mê cung + xml
    • loại MIME cụ thể của nhà cung cấp, ví dụ vnd.github + json
    • loại MIME chung với
      • vocab RDF dành riêng cho ứng dụng, ví dụ ld + json & hydra , lược đồ.org
      • hồ sơ cụ thể của ứng dụng, ví dụ: hal + json & hồ sơ liên kết hồ sơ (tôi đoán)
  • siêu liên kết
    • những gì nên chứa chúng (chọn một)
      • gửi tiêu đề liên kết
      • gửi phản hồi hypermedia, ví dụ html, nguyên tử + xml, hal + json, ld + json & hydra, v.v ...
    • ngữ nghĩa
      • sử dụng quan hệ liên kết IANA và có thể quan hệ liên kết tùy chỉnh
      • sử dụng một vocab RDF dành riêng cho ứng dụng

REST có một ràng buộc không trạng thái, tuyên bố rằng giao tiếp giữa dịch vụ REST và máy khách phải không trạng thái. Điều này có nghĩa là dịch vụ REST không thể duy trì trạng thái máy khách, do đó bạn không thể có bộ lưu trữ phiên phía máy chủ. Bạn phải xác thực từng yêu cầu. Vì vậy, ví dụ HTTP auth cơ bản (một phần của tiêu chuẩn HTTP) là được, bởi vì nó sẽ gửi tên người dùng và mật khẩu với mỗi yêu cầu.

Để trả lời bạn câu hỏi

  1. Có nó có thể được.

    Chỉ cần đề cập, các khách hàng không quan tâm đến cấu trúc IRI, họ quan tâm đến ngữ nghĩa, bởi vì họ theo các liên kết có quan hệ liên kết hoặc thuộc tính dữ liệu được liên kết (RDF).

    Điều duy nhất quan trọng về IRI, đó là một IRI duy nhất phải xác định chỉ một tài nguyên. Nó được phép cho một tài nguyên, như người dùng, có nhiều IRI khác nhau.

    Nó khá đơn giản tại sao chúng ta sử dụng IRI đẹp như /users/123/password; việc viết logic định tuyến trên máy chủ sẽ dễ dàng hơn nhiều khi bạn hiểu IRI chỉ bằng cách đọc nó.

  2. Bạn có nhiều động từ hơn, như PUT, PATCH, TÙY CHỌN và thậm chí nhiều hơn, nhưng bạn không cần thêm chúng ... Thay vì thêm các động từ mới, bạn phải học cách thêm tài nguyên mới.

    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: {}}

    (Việc đăng nhập không có ý nghĩa từ phối cảnh REST, vì các ràng buộc không trạng thái.)

  3. Người dùng của bạn không quan tâm tại sao vấn đề tồn tại. Họ chỉ muốn biết nếu có thành công hay có lỗi và có thể là thông báo lỗi mà họ có thể hiểu, ví dụ: "Xin lỗi, nhưng chúng tôi không thể lưu bài đăng của bạn.", V.v ...

    Các tiêu đề trạng thái HTTP là các tiêu đề tiêu chuẩn của bạn. Mọi thứ khác nên ở trong cơ thể tôi nghĩ. Một tiêu đề duy nhất là không đủ để mô tả ví dụ thông báo lỗi đa ngôn ngữ.

  4. Ràng buộc không trạng thái (cùng với các ràng buộc bộ đệm và hệ thống lớp) đảm bảo rằng dịch vụ mở rộng tốt. Bạn chắc chắn sẽ không duy trì hàng triệu phiên trên máy chủ, khi bạn có thể làm điều tương tự trên máy khách ...

    Máy khách bên thứ 3 nhận được mã thông báo truy cập nếu người dùng cấp quyền truy cập cho nó bằng ứng dụng khách chính. Sau đó, khách hàng bên thứ 3 gửi mã thông báo truy cập với mọi yêu cầu. Có nhiều giải pháp phức tạp hơn, ví dụ bạn có thể ký từng yêu cầu, v.v ... Để biết thêm chi tiết, hãy kiểm tra hướng dẫn sử dụng OAuth.

Tài liệu liên quan


11

Đối với các ví dụ bạn đã nêu tôi sẽ sử dụng như sau:

activ_login

POST /users/1/activation

hủy kích hoạt_login

DELETE /users/1/activation

đổi mật khẩu

PUT /passwords (điều này giả sử người dùng được xác thực)

thêm tín dụng

POST /credits (điều này giả sử người dùng được xác thực)

Đối với các lỗi bạn sẽ trả lại lỗi trong phần thân ở định dạng mà bạn nhận được yêu cầu, vì vậy nếu bạn nhận được:

DELETE /users/1.xml

Bạn sẽ gửi phản hồi lại bằng XML, điều tương tự cũng đúng với JSON, v.v ...

Để xác thực, bạn nên sử dụng xác thực http.


1
Tôi sẽ không sử dụng createnhư một phần của URI (hãy nhớ rằng URI phải là danh từ và phương thức HTTP phải là động từ hoạt động trên những danh từ đó.) Thay vào đó, tôi sẽ có một tài nguyên giống như /users/1/activemột boolean đơn giản và nó có thể là được đặt bằng cách PUTting 1 hoặc 0 cho tài nguyên đó.
Friedo

Bạn nói đúng, tôi lấy ra / tạo. Nó chỉ nên là một bài viết cho tài nguyên đơn lẻ.
jonnii

3
Tôi cũng sẽ không sử dụng activationtrên URI, trừ khi bạn sẽ thao túng và quản lý tài nguyên theo tên rõ ràng /users/1/activation. Một GET trên đó làm gì? PUT làm gì? Tôi chắc chắn rằng tôi đang xác minh URI. Ngoài ra, như đối với đàm phán kiểu nội dung, điều đó cũng thường được loại bỏ khỏi URI và được chèn vào các tiêu đề, như thế nào Accept.
Cheeso

6
  1. Sử dụng bài đăng khi bạn không biết URI tài nguyên mới trông như thế nào (bạn tạo người dùng mới, ứng dụng sẽ gán cho người dùng mới đó là id), PUT để cập nhật hoặc tạo tài nguyên mà bạn biết họ sẽ được trình bày như thế nào (ví dụ : PUT /myfiles/thisismynewfile.txt)
  2. trả về mô tả lỗi trong phần thân thông báo
  3. Bạn có thể sử dụng xác thực HTTP (nếu đủ) Các dịch vụ web phải là statele

5

Tôi muốn đề xuất (như một lần đầu tiên) PUTchỉ nên được sử dụng để cập nhật các thực thể hiện có. POSTnên được sử dụng để tạo ra cái mới. I E

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

không cảm thấy đúng với tôi. Phần còn lại của phần đầu tiên của bạn (sử dụng lại động từ) có vẻ hợp lý, tuy nhiên.


có lẽ ai đó nghĩ rằng đây thực sự không phải là một câu trả lời cho câu hỏi của anh ấy
Lubos hasko

6
Việc tôi sử dụng PUT so với POST để tạo các thực thể mới là sử dụng PUT khi người gọi kiểm soát tên tài nguyên, do đó bạn có thể PUT với tài nguyên chính xác và POST khi callee kiểm soát tên tài nguyên mới (ví dụ như trong ví dụ ở đây).
SteveD

5

Verbose, nhưng được sao chép từ đặc tả phương thức HTTP 1.1 tại http://www.w3.org/Prot Protocol / rfc2616 / rfc2616-sec9.html

9.3 NHẬN

Phương thức GET có nghĩa là lấy bất kỳ thông tin nào (dưới dạng thực thể) được xác định bởi URI yêu cầu. Nếu URI yêu cầu đề cập đến một quy trình sản xuất dữ liệu, thì đó là dữ liệu được tạo sẽ được trả về làm thực thể trong phản hồi và không phải là văn bản nguồn của quy trình, trừ khi văn bản đó là đầu ra của quy trình.

Các ngữ nghĩa của phương thức GET thay đổi thành "GET có điều kiện" nếu thông báo yêu cầu bao gồm trường tiêu đề If-Modified-Because, If-Unmodified-Because, If-Match, If-none-Match hoặc If-Range. Phương thức GET có điều kiện yêu cầu thực thể chỉ được chuyển trong các trường hợp được mô tả bởi (các) trường tiêu đề có điều kiện. Phương thức GET có điều kiện nhằm giảm việc sử dụng mạng không cần thiết bằng cách cho phép các thực thể được lưu trong bộ nhớ cache được làm mới mà không yêu cầu nhiều yêu cầu hoặc truyền dữ liệu đã được giữ bởi máy khách.

Các ngữ nghĩa của phương thức GET thay đổi thành "GET một phần" nếu thông báo yêu cầu bao gồm trường tiêu đề Phạm vi. Một phần GET yêu cầu chỉ một phần của thực thể được chuyển, như được mô tả trong phần 14,35. Phương thức GET một phần nhằm giảm việc sử dụng mạng không cần thiết bằng cách cho phép các thực thể được truy xuất một phần được hoàn thành mà không cần truyền dữ liệu do khách hàng nắm giữ.

Phản hồi cho yêu cầu GET có thể được lưu trong bộ nhớ cache khi và chỉ khi nó đáp ứng các yêu cầu cho bộ đệm HTTP được mô tả trong phần 13.

Xem phần 15.1.3 để xem xét bảo mật khi được sử dụng cho các biểu mẫu.

9,5 BÀI ĐĂNG

Phương thức POST được sử dụng để yêu cầu máy chủ gốc chấp nhận thực thể được đính kèm trong yêu cầu dưới dạng cấp dưới mới của tài nguyên được xác định bởi URI yêu cầu trong Dòng yêu cầu. POST được thiết kế để cho phép một phương thức thống nhất bao gồm các chức năng sau:

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

Hàm thực tế được thực hiện bởi phương thức POST được xác định bởi máy chủ và thường phụ thuộc vào URI yêu cầu. Thực thể được đăng phụ thuộc vào URI đó giống như cách một tệp phụ thuộc vào một thư mục chứa nó, một bài báo tin tức phụ thuộc vào một nhóm tin được đăng hoặc một bản ghi phụ thuộc vào cơ sở dữ liệu.

Hành động được thực hiện bởi phương thức POST có thể không dẫn đến một tài nguyên có thể được xác định bởi một URI. Trong trường hợp này, 200 (OK) hoặc 204 (Không có nội dung) là trạng thái phản hồi phù hợp, tùy thuộc vào việc phản hồi có bao gồm thực thể mô tả kết quả hay không.

Nếu tài nguyên đã được tạo trên máy chủ gốc, phản hồi NÊN là 201 (Đã tạo) và chứa một thực thể mô tả trạng thái của yêu cầu và tham chiếu đến tài nguyên mới và tiêu đề Vị trí (xem phần 14.30).

Các phản hồi cho phương thức này không được lưu trong bộ nhớ cache, trừ khi phản hồi bao gồm các trường tiêu đề Kiểm soát bộ đệm hoặc hết hạn thích hợp. Tuy nhiên, phản hồi 303 (Xem Khác) có thể được sử dụng để chỉ đạo tác nhân người dùng truy xuất tài nguyên có thể lưu trong bộ nhớ cache.

Yêu cầu POST PHẢI tuân theo các yêu cầu truyền thông điệp được nêu trong phần 8.2.

Xem phần 15.1.3 để xem xét bảo mật.

9,6 PUT

Phương thức PUT yêu cầu thực thể kèm theo được lưu trữ theo URI yêu cầu được cung cấp. Nếu URI yêu cầu đề cập đến một tài nguyên đã tồn tại, thực thể kèm theo NÊN được coi là phiên bản sửa đổi của tài nguyên cư trú trên máy chủ gốc. Nếu URI yêu cầu không trỏ đến tài nguyên hiện có và URI đó có khả năng được xác định là tài nguyên mới bởi tác nhân người dùng yêu cầu, máy chủ gốc có thể tạo tài nguyên với URI đó. Nếu tài nguyên mới được tạo, máy chủ gốc PHẢI thông báo cho tác nhân người dùng thông qua phản hồi 201 (Đã tạo). Nếu tài nguyên hiện có được sửa đổi, mã phản hồi 200 (OK) hoặc 204 (Không có nội dung) NÊN được gửi để cho biết hoàn thành yêu cầu thành công. Nếu tài nguyên không thể được tạo hoặc sửa đổi với URI yêu cầu, một phản hồi lỗi thích hợp NÊN đưa ra phản ánh bản chất của vấn đề. Người nhận thực thể PHẢI KHÔNG bỏ qua bất kỳ tiêu đề Content- * (ví dụ Phạm vi nội dung) nào mà nó không hiểu hoặc không thực hiện và PHẢI trả lại phản hồi 501 (Không được triển khai) trong những trường hợp như vậy.

Nếu yêu cầu chuyển qua bộ đệm và URI yêu cầu xác định một hoặc nhiều thực thể hiện được lưu trong bộ nhớ cache, các mục đó NÊN được coi là cũ. Phản hồi cho phương pháp này không được lưu trữ.

Sự khác biệt cơ bản giữa các yêu cầu POST và PUT được phản ánh theo nghĩa khác nhau của URI yêu cầu. URI trong yêu cầu POST xác định tài nguyên sẽ xử lý thực thể kèm theo. Tài nguyên đó có thể là một quá trình chấp nhận dữ liệu, một cổng vào một số giao thức khác hoặc một thực thể riêng biệt chấp nhận các chú thích. Ngược lại, URI trong yêu cầu PUT xác định thực thể kèm theo yêu cầu - tác nhân người dùng biết URI được dự định là gì và máy chủ KHÔNG cố gắng áp dụng yêu cầu cho một số tài nguyên khác. Nếu máy chủ mong muốn rằng yêu cầu được áp dụng cho một URI khác,

nó PHẢI gửi phản hồi 301 (Đã di chuyển vĩnh viễn); tác nhân người dùng CÓ THỂ đưa ra quyết định của riêng mình về việc có chuyển hướng yêu cầu hay không.

Một tài nguyên duy nhất CÓ THỂ được xác định bởi nhiều URI khác nhau. Ví dụ: một bài viết có thể có URI để xác định "phiên bản hiện tại" tách biệt với URI xác định từng phiên bản cụ thể. Trong trường hợp này, yêu cầu PUT trên URI chung có thể dẫn đến một số URI khác được xác định bởi máy chủ gốc.

HTTP / 1.1 không xác định cách thức phương thức PUT ảnh hưởng đến trạng thái của máy chủ gốc.

Yêu cầu PUT PHẢI tuân theo các yêu cầu truyền thông điệp được nêu trong phần 8.2.

Trừ khi được quy định khác cho một tiêu đề thực thể cụ thể, các tiêu đề thực thể trong yêu cầu PUT NÊN được áp dụng cho tài nguyên được tạo hoặc sửa đổi bởi PUT.

9.7 XÓA

Phương thức DELETE yêu cầu máy chủ gốc xóa tài nguyên được xác định bởi URI yêu cầu. Phương pháp này CÓ THỂ bị ghi đè bởi sự can thiệp của con người (hoặc các phương tiện khác) trên máy chủ gốc. Máy khách không thể được đảm bảo rằng thao tác đã được thực hiện, ngay cả khi mã trạng thái được trả về từ máy chủ gốc cho biết rằng hành động đã được hoàn thành thành công. Tuy nhiên, máy chủ KHÔNG NÊN chỉ ra thành công trừ khi tại thời điểm phản hồi được đưa ra, nó dự định xóa tài nguyên hoặc di chuyển nó đến một vị trí không thể truy cập.

Phản hồi thành công NÊN 200 (OK) nếu phản hồi bao gồm một thực thể mô tả trạng thái, 202 (Được chấp nhận) nếu hành động chưa được ban hành hoặc 204 (Không có nội dung) nếu hành động đã được ban hành nhưng phản hồi không bao gồm một thực thể.

Nếu yêu cầu chuyển qua bộ đệm và URI yêu cầu xác định một hoặc nhiều thực thể hiện được lưu trong bộ nhớ cache, các mục đó NÊN được coi là cũ. Phản hồi cho phương pháp này không được lưu trữ.


2

Về mã trả về REST: việc trộn mã giao thức HTTP và kết quả REST là sai .

Tuy nhiên, tôi đã thấy nhiều triển khai trộn lẫn chúng và nhiều nhà phát triển có thể không đồng ý với tôi.

Mã trả về HTTP có liên quan đến HTTP Requestchính nó. Một cuộc gọi REST được thực hiện bằng cách sử dụng yêu cầu Giao thức truyền siêu văn bản và nó hoạt động ở mức thấp hơn so với chính phương thức REST được gọi. REST là một khái niệm / cách tiếp cận và đầu ra của nó là kết quả kinh doanh / logic , trong khi mã kết quả HTTP là một phương thức vận chuyển .

Ví dụ: trả về "404 Không tìm thấy" khi bạn gọi / người dùng / bị nhầm lẫn, bởi vì điều đó có thể có nghĩa là:

  • URI sai (HTTP)
  • Không tìm thấy người dùng (REST)

"403 Bị cấm / Truy cập bị từ chối" có thể có nghĩa là:

  • Cần có sự cho phép đặc biệt Trình duyệt có thể xử lý nó bằng cách hỏi người dùng / mật khẩu. (HTTP)
  • Quyền truy cập sai được cấu hình trên máy chủ. (HTTP)
  • Bạn cần được xác thực (REST)

Và danh sách có thể tiếp tục với '500 Lỗi máy chủ "(lỗi ném HTTP / Nginx HTTP hoặc lỗi ràng buộc kinh doanh trong REST) ​​hoặc các lỗi HTTP khác, v.v ...

Từ mã, thật khó để hiểu lý do thất bại là gì, lỗi HTTP (vận chuyển) hay lỗi REST (logic).

Nếu yêu cầu HTTP được thực hiện thành công, nó sẽ luôn trả về 200 mã, bất kể (các) bản ghi có được tìm thấy hay không. Bởi vì tài nguyên URI được tìm thấy và được xử lý bởi máy chủ http. Vâng, nó có thể trả về một bộ trống. Có thể nhận được một trang web trống với 200 kết quả http, phải không?

Thay vì điều này, bạn có thể trả về 200 mã HTTP và chỉ đơn giản là một JSON với một mảng / đối tượng trống hoặc để sử dụng cờ kết quả / thành công bool để thông báo về trạng thái hoạt động được thực hiện.

Ngoài ra, một số nhà cung cấp internet có thể chặn yêu cầu của bạn và trả lại cho bạn mã 404 http. Điều này không có nghĩa là dữ liệu của bạn không được tìm thấy, nhưng có gì đó không đúng ở cấp độ vận chuyển.

Từ Wiki :

Vào tháng 7 năm 2004, nhà cung cấp dịch vụ viễn thông BT Group của Anh đã triển khai hệ thống chặn nội dung Cleanfeed, trả về lỗi 404 cho bất kỳ yêu cầu nào về nội dung được xác định là có khả năng bất hợp pháp bởi Internet Watch Foundation. Các ISP khác trả về lỗi "cấm" HTTP 403 trong cùng trường hợp. Việc sử dụng các lỗi 404 giả như một phương tiện để che giấu kiểm duyệt cũng đã được báo cáo ở Thái Lan và Tunisia. Ở Tunisia, nơi kiểm duyệt nghiêm trọng trước cuộc cách mạng năm 2011, mọi người nhận thức được bản chất của lỗi 404 giả và tạo ra một nhân vật tưởng tượng có tên "Ammar 404" đại diện cho "người kiểm duyệt vô hình".

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.