HTTP GET với thân yêu cầu


2110

Tôi đang phát triển một dịch vụ web RESTful mới cho ứng dụng của chúng tôi.

Khi thực hiện GET trên các thực thể nhất định, khách hàng có thể yêu cầu nội dung của thực thể. Nếu họ muốn thêm một số tham số (ví dụ: sắp xếp danh sách), họ có thể thêm các tham số này trong chuỗi truy vấn.

Ngoài ra, tôi muốn mọi người có thể chỉ định các tham số này trong thân yêu cầu. HTTP / 1.1 dường như không rõ ràng cấm điều này. Điều này sẽ cho phép họ chỉ định thêm thông tin, có thể giúp chỉ định các yêu cầu XML phức tạp dễ dàng hơn.

Những câu hỏi của tôi:

  • Đây có phải là một ý tưởng tốt hoàn toàn?
  • Các máy khách HTTP có gặp vấn đề với việc sử dụng các thân yêu cầu trong yêu cầu GET không?

http://tools.ietf.org/html/rfc2616


552
Ưu điểm là cho phép dễ dàng gửi các thân yêu cầu XML hoặc JSON, nó không bị hạn chế về độ dài và dễ mã hóa hơn (UTF-8).
Evert

29
Nếu những gì bạn đang theo đuổi là một phương pháp an toàn và bình thường cho phép các cơ quan yêu cầu, bạn có thể muốn xem TÌM KIẾM, ĐỀ XUẤT và BÁO CÁO. Tất nhiên không sử dụng GET và có một yêu cầu cơ thể đánh bại bộ nhớ đệm nhiều hay ít.
Julian Reschke

226
@fijiaaron: 3 năm sau, và kể từ đó tôi đã có nhiều kinh nghiệm viết các dịch vụ web. Về cơ bản, đó là tất cả những gì tôi đã làm trong vài năm qua. Tôi có thể nói một cách an toàn, đó thực sự là một ý tưởng rất tồi để thêm một cơ thể vào một yêu cầu GET. Hai câu trả lời hàng đầu đứng như một tảng đá.
Evert

26
@Ellesedil: Đơn giản chỉ cần đặt: Bất kỳ lợi thế nào tồn tại khi sử dụng GET qua POST, đều tồn tại do cách HTTP được thiết kế. Những lợi thế đó không còn tồn tại, khi bạn vi phạm tiêu chuẩn theo cách này. Do đó, chỉ còn một lý do để sử dụng GET + cơ thể yêu cầu thay vì POST: Thẩm mỹ. Đừng hy sinh thiết kế mạnh mẽ hơn thẩm mỹ.
Evert

7
Để nhấn mạnh những gì Evert đã nói: "nó không có giới hạn độ dài". Nếu GET với các tham số truy vấn của bạn phá vỡ giới hạn độ dài (năm 2048), thì có lựa chọn nào khác ngoài việc đưa thông tin chuỗi truy vấn vào một đối tượng json, ví dụ, trong phần thân của yêu cầu.
Kieran Ryan

Câu trả lời:


1725

Nhận xét của Roy Fielding về việc bao gồm một cơ thể với yêu cầu GET .

Đúng. Nói cách khác, bất kỳ thông báo yêu cầu HTTP nào cũng được phép chứa phần thân thông điệp và do đó phải phân tích các thông điệp với ý nghĩ đó. Tuy nhiên, ngữ nghĩa máy chủ cho GET, bị hạn chế sao cho một phần thân, nếu có, không có ý nghĩa ngữ nghĩa đối với yêu cầu. Các yêu cầu về phân tích cú pháp tách biệt với các yêu cầu về ngữ nghĩa phương thức.

Vì vậy, vâng, bạn có thể gửi một cơ thể với GET, và không, nó không bao giờ hữu ích để làm như vậy.

Đây là một phần của thiết kế lớp của HTTP / 1.1 sẽ trở nên rõ ràng một lần nữa khi thông số kỹ thuật được phân vùng (công việc đang diễn ra).

.... Roy

Có, bạn có thể gửi một yêu cầu với GET nhưng nó không có nghĩa gì cả. Nếu bạn cho ý nghĩa của nó bằng cách phân tích cú pháp trên máy chủ và thay đổi phản hồi của bạn dựa trên nội dung của nó , thì bạn đang bỏ qua đề xuất này trong thông số HTTP / 1.1, phần 4.3 :

[...] Nếu phương thức yêu cầu không bao gồm ngữ nghĩa được xác định cho phần thân thực thể, thì phần thân thông báo NÊN bị bỏ qua khi xử lý yêu cầu.

Và mô tả về phương thức GET trong thông số HTTP / 1.1, phần 9.3 :

Phương thức GET có nghĩa là lấy bất kỳ thông tin nào ([...]) được xác định bởi URI yêu cầu.

trong đó tuyên bố rằng phần thân yêu cầu không phải là một phần của việc xác định tài nguyên trong yêu cầu GET, chỉ là URI yêu cầu.

Cập nhật RFC2616 được tham chiếu là "thông số HTTP / 1.1" hiện đã lỗi thời. Trong năm 2014, nó đã được thay thế bởi RFC 7230-7237. Trích dẫn "phần thân thông điệp NÊN bỏ qua khi xử lý yêu cầu" đã bị xóa. Bây giờ chỉ là "Đóng khung thông báo yêu cầu độc lập với ngữ nghĩa của phương thức, ngay cả khi phương thức không xác định bất kỳ việc sử dụng nào cho nội dung thư" Trích dẫn thứ 2 "Phương thức GET có nghĩa là lấy bất kỳ thông tin nào ... được xác định bởi URI yêu cầu" đã bị xóa. - Từ một bình luận


71
Bộ nhớ đệm / proxy là hai thứ bạn có khả năng bị hỏng nhất, vâng. "Ngữ nghĩa" chỉ là một cách khác để nói "cách những người tạo ra các thành phần khác sẽ mong đợi các thành phần khác hoạt động". Nếu bạn vi phạm ngữ nghĩa, nhiều khả năng bạn sẽ thấy mọi thứ bị phá vỡ ở những nơi mà mọi người đã viết những điều mà bạn mong đợi sẽ được tôn vinh những ngữ nghĩa đó.
Stuart P. Bentley

108
Elaticsearch là một sản phẩm khá chính sử dụng các cơ quan yêu cầu HTTP trong GET. Theo hướng dẫn của họ, liệu yêu cầu HTTP có hỗ trợ có thân hay không không được xác định. Cá nhân tôi không thoải mái với việc đưa vào cơ thể yêu cầu GET, nhưng dường như họ có ý kiến ​​khác và họ phải biết họ đang làm gì. thun.co / guide / en / elaticsearch / guide / c Hiện / từ
GordonM

25
@iwein cung cấp cho các cơ quan yêu cầu GET có nghĩa là trên thực tế không phải là vi phạm thông số kỹ thuật. HTTP / 1.1 chỉ định rằng các máy chủ NÊN bỏ qua phần thân, nhưng RFC 2119 chỉ định rằng những người triển khai được phép bỏ qua các mệnh đề "NÊN" nếu họ có lý do chính đáng để làm như vậy. Thay vào đó, một khách hàng không vi phạm spec nếu nó giả định rằng việc thay đổi cơ thể GET sẽ không thay đổi các phản ứng.
Emil Lundberg

107
RFC2616 được tham chiếu là "thông số HTTP / 1.1" hiện đã lỗi thời. Trong năm 2014, nó đã được thay thế bởi RFC 7230-7237. Trích dẫn " phần thân thông điệp NÊN bỏ qua khi xử lý yêu cầu " đã bị xóa . Bây giờ chỉ là " Đóng khung thông báo yêu cầu độc lập với ngữ nghĩa của phương thức, ngay cả khi phương thức không xác định bất kỳ việc sử dụng nào cho nội dung thư " Trích dẫn thứ 2 " Phương thức GET có nghĩa là lấy bất kỳ thông tin nào ... được xác định bởi URI yêu cầu " đã bị xóa . Vì vậy, tôi đề nghị chỉnh sửa câu trả lời @Jarl
Artem Nakonechny

28
Tôi biết rằng đó là một chủ đề cũ - tôi tình cờ thấy nó. @Artem Nakonechny đúng về mặt kỹ thuật nhưng thông số kỹ thuật mới cho biết "Tải trọng trong thông báo yêu cầu GET không có ngữ nghĩa xác định; gửi phần thân tải theo yêu cầu GET có thể khiến một số triển khai hiện có từ chối yêu cầu." Vì vậy, nó vẫn không phải là một ý tưởng thực sự tốt nếu có thể tránh được.
fastcatch

289

Mặc dù bạn có thể làm điều đó, trong trường hợp không được loại trừ rõ ràng bởi đặc tả HTTP, tôi khuyên bạn nên tránh nó đơn giản vì mọi người không mong đợi mọi thứ hoạt động theo cách đó. Có nhiều giai đoạn trong chuỗi yêu cầu HTTP và trong khi chúng "hầu hết" tuân thủ thông số HTTP, điều duy nhất bạn yên tâm là chúng sẽ hoạt động như các trình duyệt web thường sử dụng. (Tôi đang nghĩ về những thứ như proxy trong suốt, máy gia tốc, bộ công cụ A / V, v.v.)

Đây là tinh thần đằng sau Nguyên tắc mạnh mẽ đại khái là "tự do trong những gì bạn chấp nhận và bảo thủ trong những gì bạn gửi", bạn không muốn đẩy ranh giới của một đặc tả mà không có lý do chính đáng.

Tuy nhiên, nếu bạn có một lý do chính đáng, hãy đi cho nó.


228
Nguyên tắc mạnh mẽ là thiếu sót. Nếu bạn tự do trong những gì bạn chấp nhận, bạn sẽ nhận được tào lao, nếu bạn có bất kỳ thành công nào về việc nhận con nuôi, chỉ vì bạn chấp nhận tào lao. Điều đó sẽ khiến bạn khó phát triển giao diện hơn. Chỉ cần nhìn vào HTML. Đó là nguyên tắc phản kháng trong hành động.
Eugene Beresovsky

27
Tôi nghĩ rằng sự thành công và rộng lớn của việc áp dụng (và lạm dụng) các giao thức nói lên giá trị của nguyên tắc mạnh mẽ.
caskey

39
Bạn đã bao giờ thử phân tích cú pháp HTML thực sự chưa? Không thể tự thực hiện được, đó là lý do tại sao hầu hết mọi người - bao gồm cả những người chơi thực sự lớn như Google (Chrome) và Apple (Safari), đã không làm điều đó mà chỉ dựa vào các triển khai hiện có (cuối cùng tất cả họ đều dựa vào KHTML của KDE). Việc sử dụng lại đó tất nhiên là tốt, nhưng bạn đã thử hiển thị html trong ứng dụng .net chưa? Đó là một cơn ác mộng, vì bạn phải nhúng một thành phần - không được quản lý - IE (hoặc tương tự), với các vấn đề và sự cố của nó, hoặc bạn sử dụng thành phần được quản lý có sẵn (trên codeplex) thậm chí không cho phép bạn chọn văn bản.
Eugene Beresovsky

6
Thông số HTTP không chỉ cho phép dữ liệu cơ thể với yêu cầu GET, mà đây còn là cách phổ biến: API _search phổ biến của công cụ ElasticSearch đề xuất các yêu cầu GET với truy vấn được đính kèm trong phần thân JSON. Như một sự nhượng bộ đối với việc triển khai máy khách HTTP chưa hoàn thành, nó cũng cho phép các yêu cầu POST ở đây.
Christian Pietsch

3
@ChristianPietsch, đó là thông lệ hiện nay. Bốn năm trước thì không. Mặc dù thông số kỹ thuật cho phép khách hàng tùy ý bao gồm (CÓ) một thực thể trong yêu cầu (phần 7), ý nghĩa của MAY được xác định trong RFC2119 và máy chủ proxy (crappy) có thể được tuân thủ cụ thể trong khi tước bỏ các thực thể trong các yêu cầu GET, cụ thể miễn là nó không gặp sự cố, nó có thể cung cấp 'chức năng bị giảm' bằng cách chuyển tiếp các tiêu đề yêu cầu và không phải là thực thể đi kèm. Tương tự như vậy, có một loạt các quy tắc về những thay đổi phiên bản PHẢI / CÓ THỂ / NÊN được thực hiện khi ủy quyền giữa các cấp độ giao thức khác nhau.
caskey

151

Bạn có thể sẽ gặp phải vấn đề nếu bạn cố gắng tận dụng bộ nhớ đệm. Các proxy sẽ không nhìn vào phần GET để xem các tham số có ảnh hưởng đến phản hồi không.


10
Sử dụng các trường tiêu đề ETag / Last-Modified theo cách này: khi sử dụng "GET có điều kiện", proxy / cache có thể hành động theo thông tin này.
jldupont

2
@jldupont Caches sử dụng sự hiện diện của trình xác nhận để biết liệu phản hồi cũ có thể được xác thực lại hay không, tuy nhiên, chúng không được sử dụng như một phần của khóa bộ đệm chính hoặc phụ.
Darrel Miller

Bạn có thể khắc phục điều đó bằng một tổng kiểm tra của cơ thể trong một tham số truy vấn
Adrian ngày

73

Cả giao diện điều khiển restclientREST đều không hỗ trợ điều này nhưng curl thì không.

Đặc tả HTTP nói trong phần 4.3

Phần thân thông báo KHÔNG được đưa vào yêu cầu nếu đặc tả của phương thức yêu cầu (phần 5.1.1) không cho phép gửi phần thân thực thể trong các yêu cầu.

Mục 5.1.1 chuyển hướng chúng tôi đến mục 9.x cho các phương pháp khác nhau. Không ai trong số họ rõ ràng cấm đưa vào nội dung thư. Tuy nhiên...

Mục 5.2 nói

Tài nguyên chính xác được xác định bởi một yêu cầu Internet được xác định bằng cách kiểm tra cả URI Yêu cầu và trường tiêu đề Máy chủ.

Phần 9.3 nói

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.

Cùng nhau đề xuất rằng khi xử lý yêu cầu GET, máy chủ không bắt buộc phải kiểm tra bất cứ thứ gì khác ngoài trường tiêu đề Request-URI và Host.

Tóm lại, thông số HTTP không ngăn bạn gửi nội dung thư bằng GET nhưng có sự mơ hồ đủ để nó không làm tôi ngạc nhiên nếu nó không được tất cả các máy chủ hỗ trợ.


2
Paw cũng có tùy chọn hỗ trợ các yêu cầu GET với các cơ quan nhưng nó phải được bật trong cài đặt.
s.Daniel

"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." Sau đó, về mặt kỹ thuật có bất hợp pháp / sai khi có điểm cuối GET có được tất cả các thực thể không? Ví dụ GET /contacts/100/addressestrả về một bộ sưu tập địa chỉ cho người có id=100.
Josh M.

Thư viện Java được bảo đảm để kiểm tra API REST không hỗ trợ yêu cầu GET với phần thân. Apache httpClient cũng không hỗ trợ nó.
Paulo Merson

Django cũng hỗ trợ phân tích cơ thể GET
Bruno Finger

60

Elaticsearch chấp nhận các yêu cầu GET với một cơ thể. Dường như đây là cách ưa thích: Hướng dẫn Elaticsearch

Một số thư viện máy khách (như trình điều khiển Ruby) có thể ghi nhật ký lệnh khóc vào thiết bị xuất chuẩn trong chế độ phát triển và nó đang sử dụng cú pháp này rộng rãi.


5
Đã tự hỏi tại sao Elaticsearch cho phép điều này. Điều đó có nghĩa là truy vấn này để đếm tất cả các tài liệu có tải trọng cho yêu cầu GET curl -XGET 'http://localhost:9200/_count?pretty' -d ' { "query": { "match_all": {} } }' tương đương với việc bao gồm tải trọng là sourceparam: curl -XGET 'http://localhost:9200/_count?pretty&source=%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D'
arun

40
Các truy vấn phức tạp có thể đạt độ dài tối đa tiêu đề http.
s.Daniel

11
Chính việc đọc tài liệu tìm kiếm đã đưa tôi đến câu hỏi này vì tôi nghĩ rằng nó được coi là thực hành tồi để bao gồm một cơ thể
PatrickWalker

1
Nó thậm chí không cần phải là một truy vấn phức tạp. Ngay cả một cuộn đơn giản cũng có thể trả về một scroll_id rất dài (trong một cụm có nhiều phân đoạn), điều này sẽ dẫn đến việc vượt quá độ dài url tối đa nếu được thêm vào đó.
Brent Hronik

11
Elaticsearch hỗ trợ cùng một yêu cầu sử dụng POST. Họ chỉ chọn cho phép một cơ thể trong một GET vì họ cảm thấy một GET chính xác hơn về mặt ngữ nghĩa so với POST khi nói đến việc truy vấn dữ liệu. Thật buồn cười là Elaticsearch được đề cập rất nhiều trong chủ đề này. Tôi sẽ không sử dụng một ví dụ (mặc dù từ một sản phẩm phổ biến) như một lý do để tuân theo thực tiễn.
DSO

33

Những gì bạn đang cố gắng đạt được đã được thực hiện trong một thời gian dài với một phương pháp phổ biến hơn nhiều và một phương pháp không dựa vào việc sử dụng tải trọng với GET.

Bạn có thể chỉ cần xây dựng kiểu tìm kiếm cụ thể của mình hoặc nếu bạn muốn RESTful hơn, hãy sử dụng một cái gì đó như OpenSearch và POST yêu cầu tới URI mà máy chủ hướng dẫn, nói / tìm kiếm. Sau đó, máy chủ có thể tạo kết quả tìm kiếm hoặc xây dựng URI cuối cùng và chuyển hướng bằng cách sử dụng 303.

Điều này có lợi thế là theo phương pháp PRG truyền thống, giúp bộ đệm trung gian lưu trữ kết quả, v.v.

Điều đó nói rằng, URI được mã hóa bằng mọi cách không phải là ASCII, và ứng dụng / x-www-form-urlencoding và multiart / form-data cũng vậy. Tôi khuyên bạn nên sử dụng điều này thay vì tạo một định dạng json tùy chỉnh khác nếu ý định của bạn là hỗ trợ các kịch bản ReSTful.


4
Bạn chỉ có thể xây dựng mediatype tìm kiếm cụ thể của bạn Bạn có thể xây dựng?
Piotr Dobrogost

2
Bằng cách đó, tôi đã nói rằng bạn có thể tạo một loại phương tiện có tên là application / vnd.myCompany.search + json sẽ chứa loại mẫu tìm kiếm mà bạn muốn khách hàng phát hành và sau đó khách hàng có thể gửi nó dưới dạng POST. Như tôi đã nhấn mạnh, đã có một loại phương tiện cho loại đó và được gọi là OpenSearch, nên sử dụng lại loại phương tiện hiện có trên tuyến tùy chỉnh khi bạn có thể triển khai kịch bản của mình với các tiêu chuẩn hiện có.
SerialSeb

14
Đó là thông minh, nhưng quá phức tạp và không hiệu quả. Bây giờ bạn phải gửi POST với tiêu chí tìm kiếm của bạn, nhận URI làm phản hồi từ POST của bạn, sau đó gửi GET với tiêu chí tìm kiếm URI đến máy chủ để lấy tiêu chí và gửi lại kết quả cho bạn. (Ngoại trừ việc bao gồm một URI trong URI về mặt kỹ thuật là không thể vì bạn không thể gửi một cái gì đó có thể có tối đa 255 ký tự trong một cái gì đó không quá 255 ký tự - vì vậy bạn phải sử dụng một bộ nhận dạng một phần và máy chủ của bạn sau đó cần biết cách giải quyết URI cho các tiêu chí tìm kiếm đã đăng của bạn.)
fijiaaron

30

Bạn có thể gửi một GET với một cơ thể hoặc gửi POST và từ bỏ tính tôn giáo RESTish (điều đó không tệ lắm, 5 năm trước chỉ có một thành viên của đức tin đó - những bình luận của anh ấy được liên kết ở trên).

Không phải là quyết định tuyệt vời, nhưng việc gửi một cơ quan GET có thể ngăn chặn sự cố cho một số khách hàng - và một số máy chủ.

Làm một POST có thể có những trở ngại với một số khung RESTish.

Julian Reschke đã đề xuất ở trên bằng cách sử dụng tiêu đề HTTP không chuẩn như "TÌM KIẾM" có thể là một giải pháp tao nhã, ngoại trừ việc nó thậm chí còn ít được hỗ trợ hơn.

Có thể là hiệu quả nhất để liệt kê các khách hàng có thể và không thể làm từng điều ở trên.

Các khách hàng không thể gửi GET với cơ thể (mà tôi biết):

  • Trình điều khiển XmlHTTPRequest

Các khách hàng có thể gửi một GET với cơ thể:

  • hầu hết các trình duyệt

Máy chủ và thư viện có thể truy xuất nội dung từ GET:

  • Apache
  • PHP

Máy chủ (và proxy) tước cơ thể khỏi GET:

  • ?

2
Mực 3.1.6 cũng loại bỏ các cơ quan NHẬN khi Độ dài nội dung bằng 0 hoặc không được đặt và nếu không sẽ gửi lại Độ dài HTTP 411 Yêu cầu ngay cả khi độ dài được đặt
rkok

2
Fiddler sẽ, nhưng nó cảnh báo bạn.
chập chững

Bạn đang nói rằng một SEARCHphương pháp có thể sẽ phá vỡ trên đường đi? Nếu proxy không hiểu một phương pháp, họ dự kiến ​​sẽ thông qua nó, vì vậy tôi không chắc tại sao bạn nghĩ nó sẽ phá vỡ bất cứ điều gì ...
Alexis Wilke

22

Tôi đặt câu hỏi này cho IETF HTTP WG. Nhận xét từ Roy Fielding (tác giả của tài liệu http / 1.1 năm 1998) là

"... một việc thực hiện sẽ bị phá vỡ để làm bất cứ điều gì khác ngoài việc phân tích và loại bỏ cơ thể đó nếu nhận được"

Các trạng thái RFC 7213 (HTTPbis):

"Một tải trọng trong một thông báo yêu cầu GET không có ngữ nghĩa được xác định;"

Bây giờ có vẻ rõ ràng rằng ý định là ý nghĩa ngữ nghĩa đối với các cơ quan yêu cầu GET bị cấm, điều đó có nghĩa là cơ thể yêu cầu không thể được sử dụng để ảnh hưởng đến kết quả.

Có những proxy ngoài đó chắc chắn sẽ phá vỡ yêu cầu của bạn theo nhiều cách khác nhau nếu bạn bao gồm một cơ thể trên GET.

Vì vậy, tóm lại, đừng làm điều đó.


21

Máy chủ nào sẽ bỏ qua nó? - fijiaaron ngày 30 tháng 8 năm 12 lúc 21:27

Google chẳng hạn đang làm tồi tệ hơn là bỏ qua nó, nó sẽ coi đó là một lỗi !

Hãy thử nó với một netcat đơn giản:

$ netcat www.google.com 80
GET / HTTP/1.1
Host: www.google.com
Content-length: 6

1234

(nội dung 1234 được theo sau bởi CR-LF, do đó tổng cộng là 6 byte)

và bạn sẽ nhận được:

HTTP/1.1 400 Bad Request
Server: GFE/2.0
(....)
Error 400 (Bad Request)
400. That’s an error.
Your client has issued a malformed or illegal request. That’s all we know.

Bạn cũng nhận được 400 Yêu cầu xấu từ Bing, Apple, v.v ... được phục vụ bởi AkamaiGhost.

Vì vậy, tôi sẽ không khuyên sử dụng các yêu cầu GET với một thực thể cơ thể.


65
Ví dụ này là vô nghĩa vì thông thường khi mọi người sẽ thêm cơ thể vào GETcác yêu cầu, đó là vì máy chủ tùy chỉnh của riêng họ có thể xử lý nó. Do đó, câu hỏi đặt ra là liệu các "bộ phận chuyển động" khác (trình duyệt, bộ đệm, v.v.) có hoạt động tốt không.
Pacerier

6
Đây là một yêu cầu xấu vì tải trọng của bạn không được mong đợi (hoặc hợp lý) cho một GET điểm cuối cụ thể đó - nó không liên quan gì đến việc sử dụng GETtrong trường hợp chung. Một tải trọng ngẫu nhiên có thể phá vỡ một POSTcách dễ dàng và trả lại như cũ 400 Bad Request, nếu nội dung không ở định dạng có ý nghĩa trong ngữ cảnh của yêu cầu cụ thể.
tộc

Và không chỉ trên toàn bộ điểm cuối đó , mà là trên URL cụ thể đó .
Lawrence Dol

1
Điều này không liên quan vì đây chỉ là triển khai máy chủ của Google tại URL đó. Vì vậy, nó không có ý nghĩa cho câu hỏi
Joel Duckworth

Đối với tôi nó rất hữu ích, vì tôi đã cố gắng sử dụng các hàm firebase với get request + body và lỗi này có thể rất khó hiểu và khó hiểu.
scrimau

19

Từ RFC 2616, phần 4.3 , "Nội dung thư":

Một máy chủ NÊN đọc và chuyển tiếp nội dung thư theo bất kỳ yêu cầu nào; nếu phương thức yêu cầu không bao gồm ngữ nghĩa được xác định cho một thực thể, thì phần thân thông báo NÊN bị bỏ qua khi xử lý yêu cầu.

Nghĩa là, các máy chủ phải luôn luôn đọc bất kỳ nội dung yêu cầu nào được cung cấp từ mạng (kiểm tra Độ dài nội dung hoặc đọc phần thân, v.v.). Ngoài ra, proxy nên chuyển tiếp bất kỳ cơ quan yêu cầu như vậy họ nhận được. Sau đó, nếu RFC định nghĩa ngữ nghĩa cho phần thân cho phương thức đã cho, thì máy chủ thực sự có thể sử dụng phần thân yêu cầu để tạo phản hồi. Tuy nhiên, nếu RFC không xác định ngữ nghĩa cho phần thân, thì máy chủ nên bỏ qua nó.

Điều này phù hợp với trích dẫn từ Fielding ở trên.

Mục 9.3 , "NHẬN", mô tả ngữ nghĩa của phương thức GET và không đề cập đến các cơ quan yêu cầu. Do đó, một máy chủ nên bỏ qua mọi yêu cầu mà nó nhận được trong yêu cầu GET.


Mục 9.5 , "POST", cũng không đề cập đến các cơ quan yêu cầu, vì vậy logic này là thiếu sót.
CarLuva

9
@CarLuva Phần POST cho biết "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ể kèm theo ..." Phần thân thực thể cho biết "Phần thân thực thể được lấy từ phần thân thông điệp ..." Do đó, Phần POST không đề cập đến nội dung thư, mặc dù gián tiếp bằng cách tham chiếu nội dung thực thể được thực hiện bởi nội dung thư của yêu cầu POST.
frederickf

9

Theo XMLHttpRequest, nó không hợp lệ. Từ tiêu chuẩn :

4.5.6 send()Phương pháp

client . send([body = null])

Bắt đầu yêu cầu. Đối số tùy chọn cung cấp phần thân yêu cầu. Đối số được bỏ qua nếu phương thức yêu cầu là GEThoặc HEAD.

Ném một InvalidStateErrorngoại lệ nếu một trong hai trạng thái không được mở hoặc send()cờ được đặt.

Các phương pháp phải chạy các bước sau:send(body)

  1. Nếu trạng thái không được mở , ném một InvalidStateErrorngoại lệ.
  2. Nếu send()cờ được đặt, ném một InvalidStateErrorngoại lệ.
  3. Nếu phương thức yêu cầu là GEThoặc HEAD, đặt thân thành null.
  4. Nếu cơ thể là null, đi đến bước tiếp theo.

Mặc dù, tôi không nghĩ vậy bởi vì yêu cầu GET có thể cần nội dung lớn.

Vì vậy, nếu bạn dựa vào XMLHttpRequest của trình duyệt, có thể nó sẽ không hoạt động.


1
bị hạ cấp do thực tế rằng XMLHttpRequest là một triển khai. Nó có thể không phản ánh các đặc điểm kỹ thuật thực tế mà nó được cho là sẽ thực hiện.
floum

10
Downvote ở trên là sai, nếu một số triển khai không hỗ trợ gửi một cơ thể bằng GET, thì đó có thể là một lý do để không làm điều đó, bất kể đặc điểm kỹ thuật. Tôi thực sự gặp phải vấn đề chính xác này trong một sản phẩm đa nền tảng mà tôi đang làm việc - chỉ có nền tảng sử dụng XMLHttpRequest không gửi được.
pjcard

8

Nếu bạn thực sự muốn gửi phần thân JSON / XML có thể lưu vào bộ nhớ cache cho ứng dụng web, nơi hợp lý duy nhất để đặt dữ liệu của bạn là chuỗi truy vấn được mã hóa bằng RFC4648: Mã hóa cơ sở 64 với URL và Tên tệp Bảng chữ cái an toàn . Tất nhiên, bạn có thể chỉ cần urlencode JSON và đặt vào giá trị của URL param, nhưng Base64 cho kết quả nhỏ hơn. Hãy nhớ rằng có các hạn chế kích thước URL, xem Độ dài tối đa của URL trong các trình duyệt khác nhau là bao nhiêu? .

Bạn có thể nghĩ rằng =ký tự đệm của Base64 có thể không tốt cho giá trị param của URL, tuy nhiên có vẻ như không - xem cuộc thảo luận này: http://mail.python.org/pipermail/python-bugs-list 2007-F/2/037195.html . Tuy nhiên, bạn không nên đặt dữ liệu được mã hóa mà không có tên param vì chuỗi được mã hóa với phần đệm sẽ được hiểu là khóa param có giá trị trống. Tôi sẽ sử dụng một cái gì đó như ?_b64=<encodeddata>.


Tôi nghĩ rằng đây là một ý tưởng khá tệ :) Nhưng nếu tôi làm điều gì đó như thế này, thay vào đó tôi sẽ sử dụng một tiêu đề HTTP tùy chỉnh (và đảm bảo rằng tôi luôn gửi lại Vary: trong phản hồi).
Evert

Xấu hay không nhưng có thể thực hiện được :) Với dữ liệu trong tiêu đề có vấn đề tương tự với kích thước dữ liệu, hãy xem stackoverflow.com/questions/686217/ . Tuy nhiên, cảm ơn vì đã đề cập đến Varytiêu đề, tôi không nhận thức được tiềm năng thực sự của nó.
gertas

6

Tôi sẽ không khuyên điều này, nó đi ngược lại các thông lệ tiêu chuẩn và không mang lại nhiều lợi ích như vậy. Bạn muốn giữ cơ thể cho nội dung, không phải tùy chọn.


5

Điều gì về các tiêu đề mã hóa base64 không phù hợp? "SOMETHINGAPP-PARAM: sdfSD45fdg45 / aS"

Hạn chế chiều dài hm. Bạn có thể làm cho việc xử lý POST của bạn phân biệt giữa các ý nghĩa không? Nếu bạn muốn các tham số đơn giản như sắp xếp, tôi không thấy lý do tại sao điều này sẽ là một vấn đề. Tôi đoán đó là sự chắc chắn mà bạn lo lắng.


Bạn có thể gửi bất kỳ tham số nào bạn muốn với x-tiền tố, mọi giới hạn về độ dài của tiêu đề hoàn toàn sẽ là giới hạn tùy ý của máy chủ.
Chris Marisic

4

Tôi rất buồn vì REST là giao thức không hỗ trợ OOP và Getphương thức là bằng chứng. Là một giải pháp, bạn có thể tuần tự hóa DTO thành JSON và sau đó tạo chuỗi truy vấn. Về phía máy chủ, bạn sẽ có thể giải tuần tự chuỗi truy vấn thành DTO.

Hãy xem:

Cách tiếp cận dựa trên thông báo có thể giúp bạn giải quyết giới hạn phương thức. Bạn sẽ có thể gửi bất kỳ DTO nào như với cơ quan yêu cầu

Khung dịch vụ web của Nelibur cung cấp chức năng mà bạn có thể sử dụng

var client = new JsonServiceClient(Settings.Default.ServiceAddress);
var request = new GetClientRequest
    {
        Id = new Guid("2217239b0e-b35b-4d32-95c7-5db43e2bd573")
    };
var response = client.Get<GetClientRequest, ClientResponse>(request);

as you can see, the GetClientRequest was encoded to the following query string

http://localhost/clients/GetWithResponse?type=GetClientRequest&data=%7B%22Id%22:%2217239b0e-b35b-4d32-95c7-5db43e2bd573%22%7D

8
Bạn chỉ nên sử dụng POST. Nếu có một tên phương thức trong url, bạn đang vi phạm thiết kế phần còn lại cơ bản. Đây là RPC, sử dụng POST.
Evert

3
Tôi không nghĩ đó là vấn đề lớn, chúng tôi gặp nhiều vấn đề hơn trong quá trình phát triển với url RESTful (tức là đơn hàng / 1). Đối với tôi, có gì đó không đúng với phương thức Get, nó không tương thích với OOP. Và ai quan tâm url trông như thế nào :) Nhưng với cách tiếp cận dựa trên thông báo, chúng ta có thể tạo giao diện từ xa ổn định và nó thực sự quan trọng. PS không phải RPC, nó dựa trên thông điệp
GSerjo

3
Tôi nghĩ rằng bạn đang thiếu toàn bộ quan điểm của REST. Khi bạn nói, ai quan tâm url trông như thế nào, REST cũng quan tâm, rất nhiều. Và tại sao REST sẽ tương thích với OOP?
shmish111

Không, tôi đã không nhìn xa hơn một chút
GSerjo

4

Ví dụ, nó hoạt động với Curl, Apache và PHP.

Tệp PHP:

<?php
echo $_SERVER['REQUEST_METHOD'] . PHP_EOL;
echo file_get_contents('php://input') . PHP_EOL;

Lệnh Console:

$ curl -X GET -H "Content-Type: application/json" -d '{"the": "body"}' 'http://localhost/test/get.php'

Đầu ra:

GET
{"the": "body"}

Thử nghiệm thú vị! PHP sẽ chỉ đọc $_POSTkhi cơ thể được gửi với một yêu cầu POST và application/x-www-form-urlencoded. Điều đó có nghĩa là cơ thể bị bỏ qua trong một GETyêu cầu. Trong trường hợp này: $_GET$_POSTdù sao cũng rất sai lệch tại thời điểm này. Vì vậy, sử dụng tốt hơnphp://input
Martin Muzatko

3

IMHO bạn chỉ có thể gửi JSONmã hóa (ví dụ. encodeURIComponent) URLTheo cách này, bạn không vi phạm HTTPthông số kỹ thuật và đưa bạn JSONđến máy chủ.


28
Vâng, nhưng vấn đề chính là giới hạn chiều dài tho, làm thế nào để chúng ta đối phó với nó?
Sabas

2

Bạn có một danh sách các tùy chọn tốt hơn nhiều so với việc sử dụng phần thân yêu cầu với GET.

Giả sử bạn có danh mục và mục cho từng danh mục. Cả hai được xác định bởi một id ("catid" / "itemid" vì lợi ích của ví dụ này). Bạn muốn sắp xếp theo một "tham số" khác trong một "thứ tự" cụ thể. Bạn muốn truyền tham số cho "sortby" và "order":

Bạn có thể:

  1. Sử dụng chuỗi truy vấn, ví dụ: example.com/category/{catid}/item/{itemid}?sortby=itemname&order=asc
  2. Sử dụng mod_rewrite (hoặc tương tự) cho các đường dẫn: example.com/category/{catid}/item/{itemid}/{sortby}/{order}
  3. Sử dụng các tiêu đề HTTP riêng lẻ mà bạn chuyển qua với yêu cầu
  4. Sử dụng một phương pháp khác, ví dụ POST, để lấy tài nguyên.

Tất cả đều có nhược điểm của chúng, nhưng tốt hơn nhiều so với sử dụng GET với cơ thể.


0

Ngay cả khi một công cụ phổ biến sử dụng điều này, như được trích dẫn thường xuyên trên trang này, tôi nghĩ nó vẫn là một ý tưởng khá tồi, quá kỳ lạ, mặc dù không bị cấm bởi thông số kỹ thuật.

Nhiều cơ sở hạ tầng trung gian có thể từ chối các yêu cầu như vậy.

Bằng ví dụ, quên về việc sử dụng một số các CDN sẵn ở phía trước của trang web của bạn, như thế này một :

Nếu GETyêu cầu người xem bao gồm một phần thân, CloudFront trả về mã trạng thái HTTP 403 (Bị cấm) cho người xem.

Và có, thư viện khách của bạn cũng có thể không hỗ trợ phát ra các yêu cầu như vậy, như đã báo cáo trong bình luận này .


0

Tôi đang sử dụng RestTemplate của khung công tác Spring trong chương trình máy khách của mình và, ở phía máy chủ, tôi đã xác định một yêu cầu GET với phần thân Json. Mục đích chính của tôi giống như của bạn: khi yêu cầu có nhiều tham số, việc đặt chúng vào cơ thể có vẻ gọn gàng hơn so với việc đưa chúng vào chuỗi URI kéo dài. Đúng?

Nhưng, thật đáng buồn, nó không hoạt động! Phía máy chủ đã ném ngoại lệ sau:

org.springframework.http.converter.HttpMessageNotReadableException: Yêu cầu cơ thể yêu cầu bị thiếu ...

Nhưng tôi khá chắc chắn rằng phần thân thông điệp được cung cấp chính xác bởi mã khách hàng của tôi, vậy có gì sai?

Tôi bắt nguồn từ phương thức RestTemplate.exchange () và tìm thấy như sau:

// SimpleClientHttpRequestFactory.class
public class SimpleClientHttpRequestFactory implements ClientHttpRequestFactory, AsyncClientHttpRequestFactory {
    ...
    protected void prepareConnection(HttpURLConnection connection, String httpMethod) throws IOException {
        ...
        if (!"POST".equals(httpMethod) && !"PUT".equals(httpMethod) && !"PATCH".equals(httpMethod) && !"DELETE".equals(httpMethod)) {
            connection.setDoOutput(false);
        } else {
            connection.setDoOutput(true);
        }
        ...
    }
}

// SimpleBufferingClientHttpRequest.class
final class SimpleBufferingClientHttpRequest extends AbstractBufferingClientHttpRequest {
    ...
    protected ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
        ...
        if (this.connection.getDoOutput() && this.outputStreaming) {
            this.connection.setFixedLengthStreamingMode(bufferedOutput.length);
        }

        this.connection.connect();
        if (this.connection.getDoOutput()) {
            FileCopyUtils.copy(bufferedOutput, this.connection.getOutputStream());
        } else {
            this.connection.getResponseCode();
        }
        ...
    }
}

Xin lưu ý rằng trong phương thức execI Internalal (), đối số đầu vào 'bufferedOutput' chứa phần thân thông điệp được cung cấp bởi mã của tôi. Tôi đã nhìn thấy nó thông qua trình gỡ lỗi.

Tuy nhiên, do readyConnection (), getDoOutput () trong execI Internalal () luôn trả về false, do đó, làm cho bufferedOutput hoàn toàn bị bỏ qua! Nó không được sao chép vào luồng đầu ra.

Do đó, chương trình máy chủ của tôi không nhận được thông báo nào và đã ném ngoại lệ đó.

Đây là một ví dụ về RestTemplate của khung công tác Spring. Vấn đề là, ngay cả khi phần thân thông điệp không còn bị cấm bởi thông số HTTP, một số thư viện hoặc khung máy chủ hoặc máy khách vẫn có thể tuân thủ thông số cũ và từ chối phần thân thông báo từ yêu cầu GET.


Bạn không đọc thông số kỹ thuật hoặc các ý kiến ​​ở đây một cách chính xác. Khách hàng và máy chủ thả cơ thể yêu cầu trong spec. Đừng sử dụng các cơ quan yêu cầu NHẬN.
Evert

@Evert Tôi không đọc bình luận chính xác hay bạn không đọc? :) Nếu bạn cuộn lên câu trả lời của Paul Morgan (câu trả lời đánh đầu) và đọc kỹ các bình luận, bạn sẽ thấy điều này: "RFC2616 được tham chiếu là" thông số HTTP / 1.1 "đã lỗi thời. Năm 2014 nó đã được thay thế bằng RFCs 7230-7237. Trích dẫn "phần thân thông điệp NÊN bị bỏ qua khi xử lý yêu cầu" đã bị xóa. Bây giờ chỉ là "Khung tin nhắn yêu cầu độc lập với ngữ nghĩa phương thức, ngay cả khi phương thức không xác định bất kỳ việc sử dụng nào cho phần thân thông điệp ... "
Chu

@Evert Hơn nữa, tôi đã sử dụng tiện ích thử nghiệm REST "yên tâm" để kiểm tra phụ trợ khởi động Spring của tôi. Cả hai bên máy chủ yên tâm và Spring-boot đều giữ thân Json cho yêu cầu GET! Chỉ RestTemplate của Sping-framework bỏ cơ thể khỏi các yêu cầu GET, vì vậy Spring-boot, yên tâm và RestTemplate, điều này có / là sai?
Chu

@Evert Cuối cùng nhưng không cho thuê, tôi đã không khuyến khích mọi người sử dụng cơ thể trong các yêu cầu GET, ngược lại, tôi đã đề nghị KHÔNG làm như vậy bằng cách phân tích mã nguồn của RestTemplate của Sping-framework, vậy tại sao bạn lại bỏ phiếu của tôi câu trả lời?
Chu

Tôi không đánh giá thấp câu trả lời của bạn. Tôi chỉ làm rõ rằng bất kỳ triển khai HTTP nào yêu cầu GET đều nằm trong spec.
Evert
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.