Câu hỏi ứng dụng / x-www-form-urlencoded hoặc multipart / form-data?


Trong HTTP có hai cách để POST dữ liệu: application/x-www-form-urlencoded và multipart/form-data. Tôi hiểu rằng hầu hết các trình duyệt chỉ có thể tải lên tệp nếu multipart/form-data Được sử dụng. Có hướng dẫn bổ sung nào khi sử dụng một trong các loại mã hóa trong ngữ cảnh API (không có trình duyệt nào liên quan) không? Điều này có thể, ví dụ: được dựa trên:

  • kích thước dữ liệu
  • sự tồn tại của các ký tự không phải ASCII
  • tồn tại trên (chưa mã hóa) dữ liệu nhị phân
  • nhu cầu chuyển dữ liệu bổ sung (như tên tệp)

Về cơ bản tôi đã không tìm thấy hướng dẫn chính thức trên web về việc sử dụng các loại nội dung khác nhau cho đến nay.


1067
2017-10-24 11:12


gốc


Cần lưu ý rằng đây là hai loại MIME mà biểu mẫu HTML sử dụng. Bản thân HTTP không có giới hạn như vậy ... người ta có thể sử dụng bất cứ loại MIME nào mà anh ta muốn thông qua HTTP. - tybro0103


Các câu trả lời:


TL; DR

Tóm lược; nếu bạn có dữ liệu nhị phân (không phải chữ và số) (hoặc tải trọng có kích thước đáng kể) để truyền, sử dụng multipart/form-data. Nếu không, hãy sử dụng application/x-www-form-urlencoded.


Các loại MIME bạn đề cập là hai Content-Type tiêu đề cho các yêu cầu HTTP POST mà tác nhân người dùng (trình duyệt) phải hỗ trợ. Mục đích của cả hai loại yêu cầu này là gửi danh sách các cặp tên / giá trị tới máy chủ. Tùy thuộc vào loại và lượng dữ liệu được truyền đi, một trong các phương pháp sẽ hiệu quả hơn phương pháp kia. Để hiểu lý do tại sao, bạn phải nhìn vào những gì mỗi người đang làm dưới sự che chở.

Dành cho application/x-www-form-urlencoded, phần thân của thông điệp HTTP được gửi đến máy chủ về cơ bản là một chuỗi truy vấn khổng lồ - các cặp tên / giá trị được phân cách bằng dấu và (&), và tên được tách biệt với các giá trị bằng biểu tượng bằng (=). Một ví dụ về điều này sẽ là:

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

Theo đặc điểm kỹ thuật:

[Dành riêng và] ký tự không phải chữ và số được thay thế bằng `% HH ', ký hiệu phần trăm và hai chữ số thập lục phân biểu thị mã ASCII của ký tự

Điều đó có nghĩa là đối với mỗi byte không phải là chữ số và chữ số tồn tại trong một trong các giá trị của chúng ta, nó sẽ mất ba byte để biểu diễn nó. Đối với các tệp nhị phân lớn, gấp ba lần tải trọng sẽ không hiệu quả cao.

Đó là nơi multipart/form-data đi vào. Với phương thức truyền cặp tên / giá trị này, mỗi cặp được biểu diễn dưới dạng một phần "" trong thông báo MIME (như được mô tả bằng các câu trả lời khác). Các bộ phận được phân cách bởi một ranh giới chuỗi cụ thể (được chọn cụ thể để chuỗi ranh giới này không xảy ra trong bất kỳ trọng tải "giá trị" nào). Mỗi phần có bộ tiêu đề MIME riêng của mình như Content-Typevà đặc biệt Content-Disposition, có thể cung cấp cho mỗi phần "tên" của nó. Phần giá trị của mỗi cặp tên / giá trị là trọng tải của mỗi phần của thông điệp MIME. Thông số MIME cung cấp cho chúng tôi nhiều tùy chọn hơn khi biểu thị trọng tải giá trị - chúng tôi có thể chọn mã hóa dữ liệu nhị phân hiệu quả hơn để tiết kiệm băng thông (ví dụ: cơ sở 64 hoặc thậm chí nhị phân thô).

Tại sao không sử dụng multipart/form-data mọi lúc? Đối với các giá trị chữ và số ngắn (giống như hầu hết các biểu mẫu web), chi phí thêm tất cả các tiêu đề MIME sẽ vượt quá đáng kể mọi khoản tiết kiệm từ mã hóa nhị phân hiệu quả hơn.


1669
2017-11-01 21:59



Liệu x-www-form-urlencoded có giới hạn độ dài hay không giới hạn? - Pacerier
@Pacerier Giới hạn được thực thi bởi máy chủ nhận yêu cầu POST. Xem chủ đề này để thảo luận thêm: stackoverflow.com/questions/2364840/… - Matt Bridges
@ZiggyTheHamster JSON và BSON hiệu quả hơn cho các loại dữ liệu khác nhau. Base64 kém hơn gzip, cho cả hai phương thức tuần tự hóa. Base64 không mang lại bất kỳ lợi thế nào cả, HTTP hỗ trợ các pyloads nhị phân. - Tiberiu-Ionuț Stan
Cũng lưu ý rằng nếu một biểu mẫu có chứa tệp tải lên có tên, lựa chọn duy nhất của bạn là biểu mẫu dữ liệu, vì urlencoded không có cách đặt tên tệp (trong biểu mẫu dữ liệu, đó là tham số tên để sắp xếp nội dung). - Guido van Rossum
@EML xem giả thuyết của tôi "(được chọn cụ thể để chuỗi ranh giới này không xảy ra trong bất kỳ trọng tải" giá trị "nào)" - Matt Bridges


ĐỌC AT LEAST PARA ĐẦU TIÊN TẠI ĐÂY!

Tôi biết đây là 3 năm quá muộn, nhưng câu trả lời của Matt (chấp nhận) là không đầy đủ và cuối cùng sẽ giúp bạn gặp rắc rối. Chìa khóa ở đây là, nếu bạn chọn sử dụng multipart/form-data, ranh giới phải không phải xuất hiện trong dữ liệu tệp mà máy chủ cuối cùng nhận được.

Đây không phải là một vấn đề cho application/x-www-form-urlencodedbởi vì không có ranh giới. x-www-form-urlencoded cũng có thể xử lý dữ liệu nhị phân, bằng cách đơn giản chuyển một byte tùy ý thành ba 7BIT byte. Không hiệu quả, nhưng nó hoạt động (và lưu ý rằng nhận xét về việc không thể gửi tên tệp cũng như dữ liệu nhị phân không đúng, bạn chỉ cần gửi nó dưới dạng cặp khóa / giá trị khác).

Vấn đề với multipart/form-data là dấu phân tách ranh giới không được có trong dữ liệu tệp (xem RFC2388; phần 5.2 cũng bao gồm một lý do khá lame vì không có một loại MIME tổng hợp thích hợp tránh được vấn đề này).

Vì vậy, ngay từ cái nhìn đầu tiên, multipart/form-data không có giá trị gì trong bất kì tải lên tệp, nhị phân hoặc cách khác. Nếu bạn không chọn ranh giới của mình một cách chính xác, thì bạn sẽ cuối cùng có một vấn đề, cho dù bạn đang gửi văn bản thuần hoặc nhị phân thô - máy chủ sẽ tìm thấy một ranh giới ở sai vị trí và tệp của bạn sẽ bị cắt bớt hoặc POST sẽ không thành công.

Điều quan trọng là chọn một mã hóa và một ranh giới sao cho các ký tự ranh giới đã chọn của bạn không thể xuất hiện trong đầu ra được mã hóa. Một giải pháp đơn giản là sử dụng base64 (làm không phải sử dụng nhị phân thô). Trong base64 3 byte tùy ý được mã hóa thành bốn ký tự 7 bit, trong đó tập hợp ký tự đầu ra là [A-Za-z0-9+/=] (ví dụ: chữ và số, hoặc '+', '/', '='). = là một trường hợp đặc biệt và chỉ xuất hiện ở cuối đầu ra được mã hóa, dưới dạng một = hoặc một đôi ==. Bây giờ, chọn ranh giới của bạn dưới dạng chuỗi ASCII 7 bit không thể xuất hiện trong base64 đầu ra. Nhiều lựa chọn bạn thấy trên mạng thất bại trong thử nghiệm này - các biểu mẫu MDN tài liệu, ví dụ, sử dụng "blob" làm ranh giới khi gửi dữ liệu nhị phân - không tốt. Tuy nhiên, một cái gì đó như "! Blob!" sẽ không bao giờ xuất hiện trong base64 đầu ra.


107
2018-04-18 11:08



Trong khi xem xét multipart / form-data là việc đảm bảo ranh giới không xuất hiện trong dữ liệu, điều này khá đơn giản để thực hiện bằng cách chọn một ranh giới đủ dài. Xin vui lòng không cho chúng tôi mã hóa base64 để thực hiện điều này. Ranh giới được tạo ngẫu nhiên và có cùng độ dài với UUID phải đủ: stackoverflow.com/questions/1705008/…. - Joshcodes
@EML, Điều này không có ý nghĩa gì cả. Rõ ràng ranh giới được chọn tự động bởi trình khách http (trình duyệt) và máy khách sẽ đủ thông minh để không sử dụng một ranh giới xung đột với nội dung của các tệp đã tải lên của bạn. Nó đơn giản như một trận đấu chuỗi con index === -1. - Pacerier
@Pacerier: (A) đọc câu hỏi: "không có trình duyệt nào liên quan, ngữ cảnh API". (B) trình duyệt không xây dựng yêu cầu cho bạn anyway. Bạn tự làm điều đó một cách thủ công. Không có phép thuật trong trình duyệt. - EML
@BeniBela, Có lẽ anh ta sẽ đề nghị sử dụng '()+-./:= sau đó. Tuy nhiên, thế hệ ngẫu nhiên với kiểm tra chuỗi con vẫn là con đường để đi và nó có thể được thực hiện với một dòng: while(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}. Đề xuất của EML (chuyển đổi sang base64 chỉ để tránh kết nối dữ liệu) chỉ đơn giản là lẻ, chưa kể đến nó đi kèm với sự suy giảm hiệu suất không cần thiết. Và tất cả những rắc rối không có gì vì thuật toán một dòng đơn giản và đơn giản. Base64 không có nghĩa là (ab) được sử dụng theo cách này, như cơ thể HTTP chấp nhận tất cả 8 bit octet. - Pacerier
Câu trả lời này không chỉ bổ sung thêm gì vào cuộc thảo luận mà còn đưa ra lời khuyên sai. Thứ nhất, bất cứ khi nào truyền dữ liệu ngẫu nhiên trong các phần riêng biệt, luôn luôn có thể ranh giới đã chọn sẽ có mặt trong tải trọng. Cách duy nhất để đảm bảo điều này không xảy ra là kiểm tra toàn bộ tải trọng cho mỗi ranh giới mà chúng tôi đưa ra. Hoàn toàn không thực tế. Chúng tôi chỉ chấp nhận vô hạn xác suất va chạm và đưa ra một ranh giới hợp lý, như "--- ranh giới- <UUID ở đây> ranh giới ---". Thứ hai, luôn luôn sử dụng Base64 sẽ lãng phí băng thông và điền vào bộ đệm không có lý do gì cả. - vagelis


Tôi không nghĩ HTTP bị giới hạn trong POST ở dạng multipart hoặc x-www-form-urlencoded. Các Tiêu đề loại nội dung là trực giao với phương thức HTTP POST (bạn có thể điền vào loại MIME phù hợp với bạn). Đây cũng là trường hợp đối với các ứng dụng web dựa trên biểu diễn HTML điển hình (ví dụ: tải trọng json trở nên rất phổ biến cho việc truyền tải tải trọng cho các yêu cầu ajax).

Về API Restful trên HTTP, các kiểu nội dung phổ biến nhất mà tôi liên lạc là application / xml và application / json.

ứng dụng / xml:

  • kích thước dữ liệu: XML rất tiết, nhưng thường không phải là vấn đề khi sử dụng nén và nghĩ rằng trường hợp truy cập ghi (ví dụ thông qua POST hoặc PUT) hiếm hơn nhiều so với truy cập đọc (trong nhiều trường hợp, <3% của tất cả lưu lượng truy cập ). Hiếm khi có trường hợp tôi phải tối ưu hóa hiệu suất ghi
  • sự tồn tại của các ký tự không phải ascii: bạn có thể sử dụng utf-8 làm mã hóa trong XML
  • sự tồn tại của dữ liệu nhị phân: sẽ cần phải sử dụng mã hóa base64
  • dữ liệu tên tệp: bạn có thể đóng gói trường bên trong này trong XML

ứng dụng / json

  • kích thước dữ liệu: ít gọn hơn XML, văn bản vẫn còn, nhưng bạn có thể nén
  • các ký tự không phải ascii: json là utf-8
  • dữ liệu nhị phân: base64 (cũng thấy json-binary-question)
  • dữ liệu tên tệp: đóng gói dưới dạng phần trường riêng bên trong json

dữ liệu nhị phân dưới dạng tài nguyên riêng

Tôi sẽ cố gắng đại diện cho dữ liệu nhị phân là tài sản / tài nguyên riêng. Nó cho biết thêm một cuộc gọi khác nhưng decouples công cụ tốt hơn. Hình ảnh ví dụ:

POST /images
Content-type: multipart/mixed; boundary="xxxx" 
... multipart data

201 Created
Location: http://imageserver.org/../foo.jpg  

Trong các tài nguyên sau này, bạn có thể chỉ cần điền vào tài nguyên nhị phân dưới dạng liên kết:

<main-resource>
 ...
 <link href="http://imageserver.org/../foo.jpg"/>
</main-resource>

82
2017-10-24 16:46



Hấp dẫn. Nhưng khi nào thì sử dụng application / x-www-form-urlencoded và khi multipart / form-data? - max
application / x-www-form-urlencoded là kiểu yêu cầu mặc định của bạn (xem thêm w3.org/TR/html401/interact/forms.html#h-17.13.4). Tôi sử dụng nó cho các biểu mẫu web "bình thường". Đối với API tôi sử dụng application / xml | json. multipart / form-data là một chuông trong suy nghĩ của các phần đính kèm (bên trong đáp ứng một số phần dữ liệu cơ thể được concattenated với một chuỗi ranh giới xác định). - manuel aldana
Tôi nghĩ rằng OP có thể chỉ hỏi về hai loại hình thức HTML sử dụng, nhưng tôi vui mừng điều này đã được chỉ ra. - tybro0103


Tôi đồng ý với nhiều điều mà Manuel đã nói. Trên thực tế, nhận xét của anh ấy đề cập đến url này ...

http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4

... có trạng thái:

Loại nội dung   "application / x-www-form-urlencoded" là   không hiệu quả để gửi lớn   số lượng dữ liệu nhị phân hoặc văn bản   chứa các ký tự không phải ASCII. Các   loại nội dung "multipart / form-data"   nên được sử dụng để gửi biểu mẫu   chứa các tệp, dữ liệu không phải ASCII,   và dữ liệu nhị phân.

Tuy nhiên, đối với tôi nó sẽ đi xuống để hỗ trợ công cụ / khung.

  • Bạn làm công cụ và khung công cụ gì mong đợi người dùng API của bạn sẽ xây dựng ứng dụng của họ với?
  • Họ có không khuôn khổ hoặc các thành phần mà họ có thể sử dụng ủng hộ một phương pháp trên khác?

Nếu bạn có ý tưởng rõ ràng về người dùng của mình và cách họ sẽ sử dụng API của bạn thì điều đó sẽ giúp bạn quyết định. Nếu bạn làm cho việc tải lên tệp khó khăn đối với người dùng API của bạn thì họ sẽ chuyển đi, bạn sẽ dành rất nhiều thời gian để hỗ trợ họ.

Trung học này sẽ là công cụ hỗ trợ BẠN có để viết API của bạn và cách dễ dàng để bạn có thể chứa một cơ chế tải lên khác.


26
2017-10-27 12:08



Xin chào, điều đó có nghĩa là mỗi lần chúng tôi đăng một số thứ lên máy chủ web, chúng tôi phải đề cập đến loại Nội dung để cho máy chủ web biết nó có nên giải mã dữ liệu không? Ngay cả khi chúng tôi tự thực hiện yêu cầu http, chúng tôi PHẢI đề cập đến đúng loại Nội dung? - GMsoF
@GMsoF, Đó là tùy chọn. Xem stackoverflow.com/a/16693884/632951 . Bạn có thể muốn tránh sử dụng kiểu nội dung khi tạo một yêu cầu cụ thể cho một máy chủ cụ thể để tránh chi phí chung. - Pacerier


Chỉ một chút gợi ý từ phía tôi khi tải lên dữ liệu hình ảnh canvas HTML5:

Tôi đang làm việc trên một dự án cho một cửa hàng in và gặp một số vấn đề do tải hình ảnh lên máy chủ đến từ HTML5 canvas thành phần. Tôi đã đấu tranh ít nhất một giờ và tôi đã không nhận được nó để lưu hình ảnh một cách chính xác trên máy chủ của tôi.

Khi tôi đặt contentType tùy chọn cuộc gọi jQuery ajax của tôi tới application/x-www-form-urlencoded mọi thứ đã đi đúng hướng và dữ liệu được mã hóa base64 được diễn giải chính xác và lưu thành công dưới dạng hình ảnh.


Có lẽ điều đó giúp một ai đó!


0
2017-12-10 15:07



Loại nội dung nào được gửi trước khi bạn thay đổi? Vấn đề này có thể là do máy chủ không hỗ trợ loại nội dung bạn đã gửi. - catorda