Các tham số được gửi trong yêu cầu POST HTTP như thế nào?


1476

Trong yêu cầu HTTP GET , các tham số được gửi dưới dạng chuỗi truy vấn :

http://example.com/page ? tham số = giá trị & cũng = khác

Trong yêu cầu POST HTTP , các tham số không được gửi cùng với URI.

Các giá trị ở đâu? Trong tiêu đề yêu cầu? Trong cơ thể yêu cầu? Nó trông như thế nào?

Câu trả lời:


1254

Các giá trị được gửi trong thân yêu cầu, theo định dạng mà kiểu nội dung chỉ định.

Thông thường loại nội dung là application/x-www-form-urlencoded, vì vậy phần thân yêu cầu sử dụng cùng định dạng với chuỗi truy vấn:

parameter=value&also=another

Khi bạn sử dụng tệp tải lên trong biểu mẫu, bạn sử dụng multipart/form-datamã hóa thay thế, có định dạng khác. Nó phức tạp hơn, nhưng bạn thường không cần quan tâm nó trông như thế nào, vì vậy tôi sẽ không đưa ra một ví dụ, nhưng thật tốt khi biết rằng nó tồn tại.


25
Tôi đã quên về việc tải lên tệp là khác nhau (+ 1 / được chấp nhận). Câu trả lời của bạn là đủ, trong khi nó sẽ rất tuyệt nếu có thêm thông tin multipart/form-data. Đối với những người quan tâm, đây là một câu hỏi về nó .
Camilo Martin

73
LƯU Ý : phần thân được tách ra khỏi tiêu đề chỉ bằng một dòng trống .
Gab 是

2
Bạn đã giải thích những gì chúng tôi đặt trong HTTPBody, nhưng chúng tôi đặt / viết gì trong HTTPHeader? Mục đích của nó là gì?
Mật ong

4
@Honey: Tiêu đề HTTP cho bài đăng trông giống như một tiêu đề, nhưng với động từ POST thay vì GET và giá trị loại nội dung (và giá trị độ dài nội dung tùy chọn) vì yêu cầu có nội dung (thân). Mỗi loại yêu cầu có một tiêu đề, một số loại cũng có một cơ thể.
Guffa

4
@KennethWorden Không, không phải phương thức nào sẽ gửi JSON đúng cách. tuy nhiên bạn có thể tải lên một tệp json theo một hình thức mã hóa với multipart/form-datahoặc nếu bạn chịu trách nhiệm xây dựng theo yêu cầu, thay đổi nội dung-type để application/jsonvà dán văn bản json trong http thân trực tiếp
Cholthi Paul Ttiopic

428

Nội dung được đặt sau các tiêu đề HTTP. Định dạng của HTTP POST là có các tiêu đề HTTP, theo sau là một dòng trống, theo sau là phần thân yêu cầu. Các biến POST được lưu trữ dưới dạng cặp khóa-giá trị trong phần thân.

Bạn có thể thấy điều này trong nội dung thô của Bài đăng HTTP, được hiển thị bên dưới:

POST /path/script.cgi HTTP/1.0
From: frog@jmarshall.com
User-Agent: HTTPTool/1.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 32

home=Cosby&favorite+flavor=flies

Bạn có thể thấy điều này bằng cách sử dụng một công cụ như Fiddler , bạn có thể sử dụng để xem yêu cầu HTTP thô và tải trọng phản hồi được gửi qua dây.


39
Chỉ khi loại nội dung là application/x-www-form-urlencoded, không phải luôn luôn như vậy.
Guffa

@ Camilo Martin .... [+1] cho câu hỏi tuyệt vời & @ Joe Alfano .... [+1] cho câu trả lời tuyệt vời ....... tôi đã có một ý tưởng rõ ràng về yêu cầu POST .... nhưng nếu một hình ảnh đi kèm với khóa, cặp giá trị của thông tin dữ liệu ..... Cấu trúc của POST trông như thế nào?
Devrath

9
@Joe, bây giờ tại sao bạn có một Fromtiêu đề ở đó?
Pacerier

@Joe, tôi thích sự bao gồm ngẫu nhiên của Fromtiêu đề. IMO nó ở trên đó với mã trạng thái 418 HTTP.
Tom Howard

Làm thế nào để bạn thêm một xác thực người dùng và mật khẩu?
m4l490n

376

Câu trả lời ngắn: trong các yêu cầu POST, các giá trị được gửi trong "phần thân" của yêu cầu. Với các biểu mẫu web, rất có thể chúng được gửi với loại phương tiện application/x-www-form-urlencodedhoặc multipart/form-data. Ngôn ngữ lập trình hoặc các khuôn khổ đã được thiết kế để xử lý web yêu cầu thường làm "The Right Thing ™" với yêu cầu đó và cung cấp cho bạn dễ dàng truy cập vào các giá trị dễ dàng giải mã (như $_REQUESThay $_POSTtrong PHP, hoặc cgi.FieldStorage(), flask.request.formtrong Python).


Bây giờ hãy lạc đề một chút, điều này có thể giúp hiểu được sự khác biệt;)

Sự khác biệt giữa GETPOSTyêu cầu phần lớn là ngữ nghĩa. Chúng cũng được "sử dụng" khác nhau, điều này giải thích sự khác biệt trong cách các giá trị được thông qua.

NHẬN ( phần RFC có liên quan )

Khi thực hiện một GETyêu cầu, bạn yêu cầu máy chủ cho một hoặc một tập hợp các thực thể. Để cho phép khách hàng lọc kết quả, nó có thể sử dụng cái gọi là "chuỗi truy vấn" của URL. Chuỗi truy vấn là phần sau ?. Đây là một phần của cú pháp URI .

Vì vậy, từ quan điểm của mã ứng dụng của bạn (phần nhận được yêu cầu), bạn sẽ cần kiểm tra phần truy vấn URI để có quyền truy cập vào các giá trị này.

Lưu ý rằng các khóa và giá trị là một phần của URI. Các trình duyệt có thể áp đặt giới hạn về độ dài URI. Tiêu chuẩn HTTP tuyên bố rằng không có giới hạn. Nhưng tại thời điểm viết bài này, hầu hết các trình duyệt đều giới hạn các URI (tôi không có giá trị cụ thể). GETyêu cầu không bao giờ được sử dụng để gửi thông tin mới đến máy chủ. Đặc biệt không phải tài liệu lớn hơn. Đó là nơi bạn nên sử dụng POSThoặc PUT.

POST ( phần RFC có liên quan )

Khi thực hiện một POSTyêu cầu, khách hàng thực sự đang gửi một tài liệu mới đến máy chủ từ xa. Vì vậy, một chuỗi truy vấn không có ý nghĩa (về mặt ngữ nghĩa). Đó là lý do tại sao bạn không có quyền truy cập vào chúng trong mã ứng dụng của mình.

POSTphức tạp hơn một chút (và cách linh hoạt hơn):

Khi nhận được yêu cầu POST, bạn phải luôn mong đợi "tải trọng" hoặc, theo thuật ngữ HTTP: nội dung thư . Bản thân phần thân thông điệp khá vô dụng, vì không có định dạng chuẩn (theo như tôi có thể nói. Có lẽ định dạng ứng dụng / octet-stream?). Định dạng cơ thể được xác định bởi Content-Typetiêu đề. Khi sử dụng một FORMphần tử HTML với method="POST", điều này thường là application/x-www-form-urlencoded. Một loại rất phổ biến khác là nhiều dữ liệu / biểu mẫu dữ liệu nếu bạn sử dụng tải lên tệp. Nhưng nó có thể là bất cứ thứ gì , từ text/plain, hơn application/jsonhoặc thậm chí là một tùy chỉnh application/octet-stream.

Trong mọi trường hợp, nếu một POSTyêu cầu được đưa ra với một yêu cầu Content-Typekhông thể được xử lý bởi ứng dụng, thì nó sẽ trả về một 415mã trạng thái .

Hầu hết các ngôn ngữ lập trình (và / hoặc khung web) cung cấp một cách để khử / mã hóa nội dung thư từ / sang các loại phổ biến nhất (như application/x-www-form-urlencoded, multipart/form-datahoặc application/json). Thật dễ dàng. Các loại tùy chỉnh yêu cầu tiềm năng công việc nhiều hơn một chút.

Sử dụng một tài liệu mã hóa biểu mẫu HTML tiêu chuẩn làm ví dụ, ứng dụng sẽ thực hiện các bước sau:

  1. Đọc Content-Typetrường
  2. Nếu giá trị không phải là một trong các loại phương tiện được hỗ trợ, thì trả về phản hồi bằng 415mã trạng thái
  3. mặt khác, giải mã các giá trị từ phần thân thông điệp.

Một lần nữa, các ngôn ngữ như PHP hoặc khung web cho các ngôn ngữ phổ biến khác có thể sẽ xử lý việc này cho bạn. Ngoại lệ cho điều này là 415lỗi. Không khung nào có thể dự đoán loại nội dung nào mà ứng dụng của bạn chọn để hỗ trợ và / hoặc không hỗ trợ. Đây là tùy thuộc vào bạn.

PUT ( phần RFC có liên quan )

Một PUTyêu cầu được xử lý khá nhiều theo cách chính xác giống như một POSTyêu cầu. Sự khác biệt lớn là một POSTyêu cầu được cho là để cho máy chủ quyết định cách (và nếu có) tạo ra một tài nguyên mới. Trong lịch sử (từ RFC2616 đã lỗi thời, nó đã tạo ra một tài nguyên mới dưới dạng "cấp dưới" (con) của URI nơi yêu cầu được gửi đến).

PUTNgược lại, một yêu cầu được cho là "ký gửi" một tài nguyên chính xác tại URI đó và với chính xác nội dung đó. Không nhiều không ít. Ý tưởng là khách hàng có trách nhiệm tạo ra tài nguyên hoàn chỉnh trước khi "PUTting" nó. Máy chủ nên chấp nhận nó như trên URL đã cho.

Kết quả là, một POSTyêu cầu thường không được sử dụng để thay thế một tài nguyên hiện có. Một PUTyêu cầu có thể làm cả tạo thay thế.

Lưu ý phụ

Ngoài ra còn có " tham số đường dẫn " có thể được sử dụng để gửi dữ liệu bổ sung đến điều khiển từ xa, nhưng chúng không phổ biến đến mức tôi sẽ không đi sâu vào chi tiết ở đây. Nhưng, để tham khảo, đây là một đoạn trích từ RFC:

Ngoài các phân đoạn dấu chấm trong các đường dẫn phân cấp, một phân đoạn đường dẫn được coi là mờ theo cú pháp chung. Các ứng dụng sản xuất URI thường sử dụng các ký tự dành riêng được cho phép trong một phân đoạn để phân định các thành phần con cụ thể theo sơ đồ hoặc trình xử lý cụ thể. Ví dụ: các ký tự dành riêng cho dấu chấm phẩy (";") và bằng ("=") thường được sử dụng để phân định các tham số và giá trị tham số áp dụng cho phân đoạn đó. Ký tự dành riêng dấu phẩy (",") thường được sử dụng cho các mục đích tương tự. Ví dụ: một nhà sản xuất URI có thể sử dụng một phân đoạn như "name; v = 1.1" để chỉ ra một tham chiếu đến phiên bản 1.1 của "name", trong khi một nhà sản xuất khác có thể sử dụng một phân đoạn như "name, 1.1" để chỉ ra điều tương tự. Các loại tham số có thể được xác định bởi ngữ nghĩa cụ thể của lược đồ,


1
Tôi có thể đã đi vào một tiếp tuyến thực sự nhẹ. Tôi đã thêm một "tl; dr" vào đầu câu trả lời để làm cho nó rõ ràng hơn.
shoutuma

Bây giờ tôi cũng đã chỉnh sửa nó để tham khảo RFC7231 thay vì RFC2616 (đã bị lỗi thời trong một thời gian). Sự khác biệt chính cho câu trả lời này ngoài các liên kết được cập nhật, là trong phần "PUT".
shoutuma

Tôi nghĩ PUT được xử lý khác với POST vì nó được coi là không có gì? stackoverflow.com/questions/611906/
trộm

1
@rogerdpack Bạn không sai. Nếu bạn đọc đoạn thứ hai trong PUTphần này, bạn sẽ thấy rằng nó idempotent. POSTngược lại có thể - theo định nghĩa - không được. POSTsẽ luôn tạo ra một nguồn tài nguyên mới. PUTsẽ, nếu một tài nguyên giống hệt tồn tại thay thế nó. Vì vậy, nếu bạn gọi POST10 lần, bạn sẽ tạo ra 10 tài nguyên. Nếu bạn gọi PUT10 lần, nó sẽ (có thể) chỉ tạo một. Câu trả lời đó có đáp ứng được câu hỏi của bạn không?
shoutuma

60

Bạn không thể nhập nó trực tiếp trên thanh URL của trình duyệt.

Bạn có thể xem cách dữ liệu POST được gửi trên Internet với Tiêu đề HTTP trực tiếp chẳng hạn. Kết quả sẽ như thế

http://127.0.0.1/pass.php
POST /pass.php HTTP/1.1

Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:18.0) Gecko/20100101 Firefox/18.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://127.0.0.1/pass.php
Cookie: passx=87e8af376bc9d9bfec2c7c0193e6af70; PHPSESSID=l9hk7mfh0ppqecg8gialak6gt5
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
username=zurfyx&pass=password

Nó nói ở đâu

Content-Length: 30
    username=zurfyx&pass=password

sẽ là giá trị bài.


2
Làm rõ: được Content-Lengthcho là 29ở đây? Đó là độ dài thực tế của chuỗi username=zurfyx&pass=password.

@Hippo là một nhân vật dòng mới có nghĩa là ở đó?
vikingsteve

@vikingsteve Tôi hiểu ý bạn. Vì vậy, tôi đoán Nội dung luôn có một dòng mới ở cuối của nó, sau đó.

2
Tiêu đề được tách ra khỏi cơ thể với dòng mới bổ sung
Mára Toner

24

Loại phương tiện mặc định trong một yêu cầu POST là application/x-www-form-urlencoded. Đây là một định dạng để mã hóa các cặp khóa-giá trị. Các phím có thể được nhân đôi. Mỗi cặp khóa-giá trị được phân tách bằng một &ký tự và mỗi khóa được phân tách khỏi giá trị của nó bằng một =ký tự.

Ví dụ:

Name: John Smith
Grade: 19

Được mã hóa thành:

Name=John+Smith&Grade=19

Điều này được đặt trong thân yêu cầu sau các tiêu đề HTTP.


1
Bạn đã giải thích những gì chúng tôi đặt trong HTTPBody, nhưng chúng tôi đặt / viết gì trong HTTPHeader?
Mật ong

Bạn đã đề cập đến chìa khóa có thể trùng lặp, vậy kết quả của một bản sao đó là gì? Liệu cái cuối cùng sẽ tự động ghi đè (các) giá trị trước đó? Cảm ơn.
Jinghui Niu

@JinghuiNiu nếu khóa bị trùng lặp thì nên phân tích cú pháp thành một mảng. Điều này là rất muộn nhưng có thể giúp đỡ người khác.
Hanash Yaslem

18

Các giá trị biểu mẫu trong HTTP POST được gửi trong thân yêu cầu, có cùng định dạng với chuỗi truy vấn.

Để biết thêm thông tin, xem thông số kỹ thuật .


5
"Cùng định dạng" là một chút mơ hồ. Họ có bắt đầu với một ?ví dụ không?
Camilo Martin

7
@PeterWooster Có, nhưng không cung cấp một ví dụ. Về vấn đề đó, giống như một câu trả lời có nội dung "nhìn này, có câu trả lời cho câu hỏi của bạn trong blog (liên kết) của ứng dụng ".
Camilo Martin

36
@PeterWooster Không cần thiết, nhưng sẽ rất tốt khi bạn quên thứ gì đó, google nó, đi đến liên kết đầu tiên là SO và có một ví dụ rõ ràng, ngắn gọn cho bạn biết những gì bạn cần thay vì gửi cho bạn để nhai thông số kỹ thuật quá chi tiết rằng, ngay cả khi toàn diện, có thể không phù hợp cho người làm mới. Hãy suy nghĩ về điều này: hầu hết các QA trên trang web này có thể sôi sục để "đọc thông số / hướng dẫn / API / etc (link) ". Nó sẽ hữu ích? Không hơn Google.
Camilo Martin

2
Chỉ khi loại nội dung là application/x-www-form-urlencoded, không phải luôn luôn như vậy.
Guffa

3
Định dạng của chuỗi truy vấn GET khác với định dạng của ứng dụng / x-www-form-urlencoding. Ví dụ: khoảng trắng được mã hóa khác nhau (% 20 so với +). Câu trả lời là sai lệch về vấn đề này.
UnclickableCharacter

18

Một số dịch vụ web yêu cầu bạn đặt riêng dữ liệu yêu cầu và siêu dữ liệu . Ví dụ, một hàm từ xa có thể mong đợi rằng chuỗi siêu dữ liệu đã ký được bao gồm trong URI, trong khi dữ liệu được đăng trong phần thân HTTP.

Yêu cầu POST có thể trông giống như thế này:

POST /?AuthId=YOURKEY&Action=WebServiceAction&Signature=rcLXfkPldrYm04 HTTP/1.1
Content-Type: text/tab-separated-values; charset=iso-8859-1
Content-Length: []
Host: webservices.domain.com
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: identity
User-Agent: Mozilla/3.0 (compatible; Indy Library)

name    id
John    G12N
Sarah   J87M
Bob     N33Y

Cách tiếp cận này kết hợp một cách hợp lý QueryString và Body-Post bằng cách sử dụng Content-Typemột "hướng dẫn phân tích cú pháp" cho máy chủ web.

Xin lưu ý: HTTP / 1.1 được bao bọc bằng #32(khoảng trắng) ở bên trái và với #10(Nguồn cấp dữ liệu) ở bên phải.


Sự khác biệt giữa /user/john/?user=johnchỉ đơn thuần là một ngữ nghĩa (HTTP không thực sự mang lại sự đối xử đặc biệt cho các chuỗi truy vấn), vì vậy tôi coi đây là điều được mong đợi một cách hợp lý. Nhưng ý bạn là gì khi "bao bọc bởi không gian bên trái"? Không có khoảng trắng trước phương thức HTTP. Bạn có nghĩa là dòng trống cho cơ thể bài?
Camilo Martin

Có một khoảng trắng (ASCII # 32) giữa ...Ym04HTTP/1.1trong đoạn mã trên. Vì vậy, QueryString chỉ đơn giản nằm giữa động từ và phiên bản giao thức.
Giao diện không xác định

1
Ghi chú của bạn làm cho nó có vẻ như là một cái gì đó bất ngờ và phiên bản cụ thể. Thẳng thắn mà nói có vẻ như có một không gian ở đó. Và nguồn cấp dữ liệu cũng áp dụng cho các dòng khác, giống như tất cả mọi thứ unix.
Camilo Martin

1
Tôi chỉ nhấn mạnh những gì tôi không thể đánh dấu trong mã. Nó có vẻ rõ ràng nhưng đôi khi không.
Giao diện không xác định

Đúng là chúng ta có thể vượt qua các tham số truy vấn như một phần của URL bằng cách tách URI và các tham số ?giống như chúng ta làm với GETcác yêu cầu.
vào

8

Trước hết, hãy phân biệt giữa GETPOST

Nhận: Đó là HTTPyêu cầu mặc định được thực hiện cho máy chủ và được sử dụng để truy xuất dữ liệu từ máy chủ và chuỗi truy vấn đi sau ?trong một URIđược sử dụng để truy xuất tài nguyên duy nhất.

đây là định dạng

GET /someweb.asp?data=value HTTP/1.0

đây data=valuelà giá trị chuỗi truy vấn được thông qua.

POST: Nó được sử dụng để gửi dữ liệu đến máy chủ một cách an toàn vì vậy bất cứ điều gì cần thiết, đây là định dạng của một POSTyêu cầu

POST /somweb.aspHTTP/1.0
Host: localhost
Content-Type: application/x-www-form-urlencoded //you can put any format here
Content-Length: 11 //it depends
Name= somename

Tại sao POST qua GET?

Trong GETgiá trị được gửi đến các máy chủ thường được thêm vào URL cơ sở trong chuỗi truy vấn, bây giờ có 2 hậu quả của việc này

  • Các GETyêu cầu được lưu trong lịch sử trình duyệt với các tham số. Vì vậy, mật khẩu của bạn vẫn chưa được mã hóa trong lịch sử trình duyệt. Đây là một vấn đề thực sự cho Facebook trở lại trong những ngày này.
  • Thông thường các máy chủ có giới hạn về thời gian URIcó thể. Nếu có quá nhiều tham số được gửi, bạn có thể nhận được414 Error - URI too long

Trong trường hợp bài đăng yêu cầu dữ liệu của bạn từ các trường được thêm vào cơ thể thay thế. Độ dài của thông số yêu cầu được tính toán và được thêm vào tiêu đề cho độ dài nội dung và không có dữ liệu quan trọng nào được thêm trực tiếp vào URL.

Bạn có thể sử dụng phần mạng của Công cụ dành cho nhà phát triển của Google để xem thông tin cơ bản về cách yêu cầu được thực hiện đối với máy chủ.

và bạn luôn có thể thêm giá trị trong bạn Request Headersnhư Cache-Control, Origin, Accept.


4
Các giả định về bảo mật chỉ đúng trong bối cảnh HTTPSkết nối, không phải HTTP. HTTPSmã hóa cả URL(bao gồm các tham số truy vấn) và Request Body, khi HTTPmã hóa / bảo vệ không. Vấn đề được mô tả xuất phát từ việc nhiều trình duyệt lưu trữ URIs(bao gồm URLs) trong cơ sở dữ liệu lịch sử của họ (thường không được mã hóa). Vì vậy, chỉ sử dụng Request Body+ HTTPScho bất cứ điều gì nhạy cảm.
Petru Zaharia

@PetruZaharia Tôi đồng ý với lời giải thích của bạn. Bạn cũng có thể đề nghị điều này như chỉnh sửa và tôi sẽ vui lòng chấp nhận! :)
Zeeshan Adil
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.