application / x-www-form-urlencoded หรือ multipart / form-data?


1335

ใน HTTP มีสองวิธีข้อมูลโพสต์: และapplication/x-www-form-urlencoded multipart/form-dataฉันเข้าใจว่าเบราว์เซอร์ส่วนใหญ่สามารถอัปโหลดไฟล์ได้เฉพาะเมื่อmultipart/form-dataมีการใช้งาน มีคำแนะนำเพิ่มเติมใดบ้างเมื่อใช้ประเภทการเข้ารหัสหนึ่งประเภทในบริบท API (ไม่มีเบราว์เซอร์ที่เกี่ยวข้อง) เช่นนี้อาจจะขึ้นอยู่กับ:

  • ขนาดข้อมูล
  • มีอักขระที่ไม่ใช่ ASCII อยู่
  • มีอยู่บน (ไม่ได้เข้ารหัส) ข้อมูลไบนารี
  • ความต้องการในการถ่ายโอนข้อมูลเพิ่มเติม (เช่นชื่อไฟล์)

ฉันพบว่าไม่มีคำแนะนำอย่างเป็นทางการบนเว็บเกี่ยวกับการใช้งานประเภทเนื้อหาที่แตกต่างกัน


74
ควรกล่าวถึงว่าสิ่งเหล่านี้เป็น MIME สองประเภทที่ใช้รูปแบบ HTML HTTP นั้นไม่มีข้อ จำกัด ดังกล่าว ... เราสามารถใช้ MIME ประเภทใดก็ได้ที่เขาต้องการผ่านทาง HTTP
tybro0103

คำตอบ:


2013

TL; DR

สรุป; ถ้าคุณมีข้อมูลไบนารี (ไม่ใช่ตัวเลข) (หรือน้ำหนักบรรทุกขนาดใหญ่อย่างมีนัยสำคัญ) multipart/form-dataเพื่อส่งใช้ application/x-www-form-urlencodedมิฉะนั้นการใช้งาน


ประเภท MIME ที่คุณพูดถึงเป็นสองContent-Typeส่วนหัวสำหรับคำขอ HTTP POST ที่ user-agent (เบราว์เซอร์) ต้องสนับสนุน วัตถุประสงค์ของคำขอทั้งสองประเภทนี้คือการส่งรายการคู่ชื่อ / ค่าไปยังเซิร์ฟเวอร์ วิธีการหนึ่งจะมีประสิทธิภาพมากกว่าวิธีอื่นทั้งนี้ขึ้นอยู่กับชนิดและปริมาณของข้อมูลที่ส่ง เพื่อทำความเข้าใจว่าทำไมคุณต้องดูว่าแต่ละคนทำอะไรภายใต้ผ้าห่ม

สำหรับapplication/x-www-form-urlencodedเนื้อความของข้อความ HTTP ที่ส่งไปยังเซิร์ฟเวอร์นั้นเป็นหนึ่งในสตริงการสืบค้นขนาดยักษ์หนึ่งคู่ - ชื่อ / ค่าจะถูกคั่นด้วยเครื่องหมายและ ( &) และชื่อจะถูกแยกออกจากค่าด้วยสัญลักษณ์เท่ากับ ( =) ตัวอย่างของสิ่งนี้จะเป็น: 

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

ตามข้อกำหนด :

[สงวนและ] อักขระที่ไม่ใช่ตัวอักษรและตัวเลขจะถูกแทนที่ด้วย `% HH 'เครื่องหมายเปอร์เซ็นต์และเลขฐานสิบหกสองหลักแทนรหัส ASCII ของอักขระ

นั่นหมายความว่าสำหรับแต่ละไบต์ที่ไม่ใช่ตัวอักษรและตัวเลขที่มีอยู่ในหนึ่งในค่าของเรามันจะใช้เวลาสามไบต์เพื่อเป็นตัวแทนของมัน สำหรับไฟล์ไบนารี่ขนาดใหญ่การเพิ่มปริมาณข้อมูลสามเท่าจะไม่มีประสิทธิภาพสูง

นั่นคือที่multipart/form-dataมาด้วยวิธีนี้ในการส่งคู่ชื่อ / ค่าแต่ละคู่จะแสดงเป็น "ส่วน" ในข้อความ MIME (ตามที่อธิบายโดยคำตอบอื่น ๆ ) ชิ้นส่วนถูกคั่นด้วยขอบเขตสตริงเฉพาะ (เลือกโดยเฉพาะเพื่อไม่ให้สตริงขอบเขตนี้เกิดขึ้นในส่วนของข้อมูล "ค่า") แต่ละส่วนมีชุดส่วนหัว MIME ของตัวเองContent-Typeและโดยเฉพาะอย่างยิ่งContent-Dispositionซึ่งสามารถตั้งชื่อ "ชื่อ" ให้แต่ละส่วนได้ ชิ้นส่วนมูลค่าของคู่ชื่อ / ค่าแต่ละคู่คือส่วนของข้อมูลของข้อความ MIME ข้อมูลจำเพาะ MIME ช่วยให้เรามีตัวเลือกมากขึ้นเมื่อแสดงถึงส่วนของข้อมูลที่คุ้มค่า - เราสามารถเลือกการเข้ารหัสข้อมูลไบนารีที่มีประสิทธิภาพยิ่งขึ้นเพื่อประหยัดแบนด์วิดท์ (เช่นฐาน 64 หรือแม้แต่ไบนารีดิบ)

ทำไมไม่ใช้multipart/form-dataตลอดเวลา? สำหรับค่าตัวอักษรและตัวเลขสั้น ๆ (เช่นเว็บฟอร์มส่วนใหญ่) ค่าใช้จ่ายในการเพิ่มส่วนหัว MIME ทั้งหมดจะมีค่ามากกว่าการออมอย่างมีนัยสำคัญจากการเข้ารหัสไบนารีที่มีประสิทธิภาพมากขึ้น


84
x-www-form-urlencoded มีขีดจำกัดความยาวหรือไม่ จำกัด
Pacerier

34
@Pacerier ข้อ จำกัด ถูกบังคับใช้โดยเซิร์ฟเวอร์ที่ได้รับคำขอ POST ดูหัวข้อนี้สำหรับการสนทนาเพิ่มเติม: stackoverflow.com/questions/2364840/…
Matt Bridges

5
@ZiggyTheHamster JSON และ BSON มีประสิทธิภาพมากขึ้นสำหรับข้อมูลประเภทต่างๆ Base64 ด้อยกว่า gzip สำหรับวิธีการทำให้เป็นอันดับทั้งสอง Base64 ไม่ก่อให้เกิดประโยชน์ใด ๆ เลย HTTP รองรับไบนารี pyloads
Tiberiu-Ionuț Stan

16
โปรดทราบว่าหากฟอร์มมีการอัปโหลดไฟล์ที่มีชื่อตัวเลือกเดียวของคุณคือ form-data เนื่องจาก urlencoded ไม่มีวิธีวางชื่อไฟล์ (ในรูปแบบข้อมูลเป็นพารามิเตอร์ชื่อเป็น content-disposition)
Guido van Rossum

4
@EML เห็น parenthetical ของฉัน "(เลือกโดยเฉพาะเพื่อไม่ให้สตริงขอบเขตนี้ไม่ปรากฏในส่วนของ" value "payloads)"
Matt Bridges

151

อ่านอย่างน้อย PARA แรกที่นี่!

ฉันรู้ว่านี่มันสายไปแล้ว 3 ปี แต่คำตอบ (ยอมรับแล้ว) ของ Matt ไม่สมบูรณ์และในที่สุดก็จะทำให้คุณเดือดร้อน ที่สำคัญคือถ้าคุณเลือกใช้multipart/form-dataขอบเขตต้องไม่ปรากฏในข้อมูลไฟล์ที่เซิร์ฟเวอร์ได้รับในที่สุด

นี่ไม่ใช่ปัญหาapplication/x-www-form-urlencodedเพราะไม่มีขอบเขต x-www-form-urlencodedสามารถจัดการข้อมูลไบนารี่ได้โดยง่ายด้วยการเปลี่ยนหนึ่งไบต์ให้เป็นสาม7BITไบต์ ไม่มีประสิทธิภาพ แต่ใช้งานได้ (และโปรดทราบว่าความคิดเห็นเกี่ยวกับการไม่สามารถส่งชื่อไฟล์รวมทั้งข้อมูลไบนารีไม่ถูกต้องคุณเพียงส่งเป็นคู่คีย์ / ค่าอื่น)

ปัญหาที่เกิดขึ้นmultipart/form-dataก็คือตัวคั่นขอบเขตจะต้องไม่ปรากฏในข้อมูลไฟล์ (ดูRFC 2388 ; ส่วน 5.2 ยังมีข้อแก้ตัวที่ค่อนข้างอ่อนแอสำหรับการไม่มีประเภท MIME รวมที่เหมาะสมที่หลีกเลี่ยงปัญหานี้)

ดังนั้นเมื่อแรกพบmultipart/form-dataไม่มีค่าใด ๆ ในการอัปโหลดไฟล์ไบนารีหรืออื่น ๆ หากคุณเลือกขอบเขตไม่ถูกต้องในที่สุดคุณก็จะมีปัญหาไม่ว่าคุณจะส่งข้อความธรรมดาหรือไบนารีแบบดิบ - เซิร์ฟเวอร์จะหาขอบเขตผิดที่และไฟล์ของคุณจะถูกตัดทอนหรือ POST จะล้มเหลว

กุญแจสำคัญคือการเลือกการเข้ารหัสและขอบเขตที่อักขระขอบเขตที่คุณเลือกไม่สามารถปรากฏในผลลัพธ์ที่เข้ารหัส หนึ่งวิธีง่ายๆคือการใช้งานbase64(ไม่ได้ใช้ไบนารีดิบ) ในbase64 3 ไบต์แบบสุ่มจะถูกเข้ารหัสเป็นอักขระ 7 บิตสี่ตัวซึ่งชุดอักขระเอาต์พุตคือ[A-Za-z0-9+/=](เช่นตัวอักษรและตัวเลข '+', '/' หรือ '=') =เป็นกรณีพิเศษและอาจจะปรากฏเฉพาะในตอนท้ายของการส่งออกที่เข้ารหัสที่เป็นหนึ่งเดียวหรือสองครั้ง= ==ตอนนี้เลือกขอบเขตของคุณเป็นสตริง ASCII 7 บิตซึ่งไม่สามารถปรากฏในbase64เอาท์พุท ตัวเลือกมากมายที่คุณเห็นบนเน็ตล้มเหลวการทดสอบนี้ - MDN เป็นเอกสารตัวอย่างเช่นใช้ "blob" เป็นขอบเขตเมื่อส่งข้อมูลไบนารี - ไม่ดี อย่างไรก็ตามบางอย่างเช่น "! blob!" จะไม่ปรากฏในbase64ผลลัพธ์


52
ในขณะที่การพิจารณาของ multipart / form-data คือการทำให้แน่ใจว่าขอบเขตไม่ปรากฏในข้อมูลซึ่งค่อนข้างง่ายที่จะทำให้สำเร็จโดยการเลือกขอบเขตที่มีความยาวเพียงพอ โปรดอย่าใช้การเข้ารหัส base64 เพื่อทำสิ่งนี้ให้สำเร็จ ขอบเขตที่สร้างแบบสุ่มและยาวเช่นเดียวกับ UUID ควรจะเพียงพอ: stackoverflow.com/questions/1705008/...
Joshcodes

20
@EML มันไม่สมเหตุสมผลเลย เห็นได้ชัดว่าขอบเขตถูกเลือกโดยอัตโนมัติโดยไคลเอนต์ http (เบราว์เซอร์) และไคลเอ็นต์จะฉลาดพอที่จะไม่ใช้ขอบเขตที่ขัดแย้งกับเนื้อหาของไฟล์ที่คุณอัปโหลด มันเป็นเรื่องง่าย AA index === -1แข่งขันย่อย
Pacerier

13
@Pacerier: (A) อ่านคำถาม: "ไม่เกี่ยวข้องกับเบราว์เซอร์บริบท API" (B) เบราว์เซอร์ไม่สร้างคำขอให้คุณ คุณทำด้วยตัวเองด้วยตนเอง ไม่มีความมหัศจรรย์ในเบราว์เซอร์
EML

12
@BeniBela เขาอาจจะแนะนำให้ใช้'()+-./:=แล้ว แต่รุ่นสุ่มที่มี substring while(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}การตรวจสอบยังคงเป็นวิธีที่จะไปและมันก็สามารถทำได้ด้วยหนึ่งบรรทัด: ข้อเสนอแนะของ EML (แปลงเป็น base64 เพียงเพื่อหลีกเลี่ยงการจับคู่สตริงย่อย) เป็นเพียงคี่ธรรมดาไม่พูดถึงมันมาพร้อมกับการเสื่อมประสิทธิภาพที่ไม่จำเป็น และปัญหาทั้งหมดที่เกิดขึ้นเพราะอัลกอริทึมหนึ่งบรรทัดนั้นไม่ซับซ้อนและไม่ซับซ้อน Base64 ไม่ได้หมายถึงเป็น (ab) ใช้วิธีนี้เนื่องจากเนื้อความ HTTP ยอมรับอ็อกเท็ต8 บิตทั้งหมด
Pacerier

31
คำตอบนี้ไม่เพียง แต่เพิ่มอะไรในการอภิปราย แต่ยังให้คำแนะนำที่ผิด ประการแรกเมื่อใดก็ตามที่ส่งข้อมูลแบบสุ่มในส่วนที่แยกจากกันก็เป็นไปได้เสมอว่าขอบเขตที่เลือกจะปรากฏในส่วนของข้อมูล วิธีเดียวที่จะทำให้แน่ใจว่าสิ่งนี้จะไม่เกิดขึ้นคือการตรวจสอบน้ำหนักบรรทุกทั้งหมดของแต่ละขอบเขตที่เราพบ ทำไม่ได้อย่างสมบูรณ์ เราเพียงแค่ยอมรับเล็กน่าจะเป็นของการปะทะกันและมากับขอบเขตที่เหมาะสมเช่น "--- boundary- <UUID ที่นี่> -boundary ---" ประการที่สองการใช้ Base64 จะเสียแบนด์วิดท์และเติมบัฟเฟอร์โดยไม่มีเหตุผลเลย
คลอด

92

ฉันไม่คิดว่า HTTP จำกัด เพียง POST ในหลายส่วนหรือ x-www-form-urlencoded ชนิดเนื้อหาส่วนหัวเป็นมุมฉากกับวิธี HTTP POST (คุณสามารถเติมชนิดไมม์ที่เหมาะสมกับคุณ) นี่เป็นกรณีของ webapps ที่ใช้ HTML แทน (เช่น json payload กลายเป็นที่นิยมอย่างมากในการส่ง payload สำหรับการร้องขอ ajax)

เกี่ยวกับ Restful API ผ่าน HTTP ประเภทเนื้อหายอดนิยมที่ฉันติดต่อด้วยคือ application / xml และ application / json

แอพลิเคชัน / XML:

  • data-size: XML verbose มาก แต่มักจะไม่เป็นปัญหาเมื่อใช้การบีบอัดและคิดว่ากรณีการเข้าถึงการเขียน (เช่นผ่าน POST หรือ PUT) หายากกว่าการเข้าถึงการอ่าน (ในหลาย ๆ กรณีมันเป็น <3% ของปริมาณการใช้ทั้งหมด ) ไม่ค่อยมีกรณีที่ฉันต้องเพิ่มประสิทธิภาพการเขียน
  • การมีอยู่ของตัวอักษรที่ไม่ใช่ ASCII: คุณสามารถใช้ utf-8 เป็นการเข้ารหัสใน XML
  • การมีอยู่ของข้อมูลไบนารี: จะต้องใช้การเข้ารหัส base64
  • ข้อมูลชื่อไฟล์: คุณสามารถสรุปในฟิลด์นี้ได้ใน XML

แอพลิเคชัน / JSON

  • ขนาดข้อมูล: กะทัดรัดน้อยกว่า XML นั้นเป็นข้อความ แต่คุณสามารถบีบอัดได้
  • ตัวอักษรที่ไม่ใช่ ascii: json คือ utf-8
  • ข้อมูลไบนารี: base64 (โปรดดูjson-binary-question )
  • ข้อมูลชื่อไฟล์: แค็ปซูลเป็นส่วนฟิลด์ของตัวเองภายใน json

ข้อมูลไบนารีเป็นทรัพยากรของตัวเอง

ฉันจะพยายามแสดงข้อมูลไบนารีเป็นสินทรัพย์ / ทรัพยากรของตัวเอง มันเพิ่มอีกสาย แต่สิ่งที่ดีกว่า decouples ภาพตัวอย่าง:

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

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

ในภายหลังทรัพยากรคุณสามารถ inline ทรัพยากรไบนารีเป็นลิงค์:

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

น่าสนใจ แต่เมื่อไรที่ต้องใช้ application / x-www-form-urlencoded และเมื่อ multipart / form-data?
สูงสุด

3
application / x-www-form-urlencoded เป็นประเภท mime เริ่มต้นของคำขอของคุณ (ดูw3.org/TR/html401/interact/forms.html#h-17.13.4 ) ฉันใช้สำหรับเว็บฟอร์ม "ปกติ" สำหรับ API ฉันใช้ application / xml | json multipart / form-data เป็นระฆังในการคิดของสิ่งที่แนบมา (ภายในร่างกายตอบสนองข้อมูลหลายส่วนจะถูกตัดแบ่งด้วยสตริงที่กำหนดขอบเขต)
manuel aldana

4
ฉันคิดว่า OP น่าจะเป็นแค่การถามเกี่ยวกับสองประเภทที่ใช้ในรูปแบบ HTML แต่ฉันดีใจที่สิ่งนี้ชี้ให้เห็น
tybro0103

30

ฉันเห็นด้วยกับสิ่งที่มานูเอลพูดมาก ในความเป็นจริงความคิดเห็นของเขาอ้างถึง URL นี้ ...

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

... ซึ่งระบุว่า:

ประเภทเนื้อหา "application / x-www-form-urlencoded" นั้นไม่มีประสิทธิภาพในการส่งข้อมูลไบนารีหรือข้อความจำนวนมากที่มีอักขระที่ไม่ใช่ ASCII ควรใช้ชนิดเนื้อหา "multipart / form-data" สำหรับการส่งแบบฟอร์มที่มีไฟล์ข้อมูลที่ไม่ใช่ ASCII และข้อมูลไบนารี

อย่างไรก็ตามสำหรับฉันมันจะลงมาที่การสนับสนุนเครื่องมือ / กรอบ

  • เครื่องมือและกรอบงานใดที่คุณคาดหวังให้ผู้ใช้ API สร้างแอพด้วย
  • พวกเขามีกรอบหรือส่วนประกอบที่พวกเขาสามารถใช้ที่ชอบวิธีหนึ่งมากกว่าที่อื่น ๆ ?

หากคุณมีความคิดที่ชัดเจนเกี่ยวกับผู้ใช้ของคุณและวิธีที่พวกเขาจะใช้ประโยชน์จาก API ของคุณนั่นจะช่วยให้คุณตัดสินใจ หากคุณทำให้การอัปโหลดไฟล์เป็นเรื่องยากสำหรับผู้ใช้ API ของคุณพวกเขาจะย้ายออกไปคุณจะใช้เวลามากในการสนับสนุนพวกเขา

รองจากสิ่งนี้จะเป็นเครื่องมือที่สนับสนุนคุณในการเขียน API ของคุณและมันง่ายเพียงใดที่คุณจะรองรับกลไกการอัพโหลดหนึ่งอันเหนือกว่า


1
สวัสดีหมายความว่าทุกครั้งที่เราโพสต์สิ่งใดสิ่งหนึ่งไปยังเว็บเซิร์ฟเวอร์เราต้องพูดถึงประเภทของเนื้อหาเพื่อให้เว็บเซิร์ฟเวอร์รู้ว่ามันควรจะถอดรหัสข้อมูลหรือไม่ แม้ว่าเราจะสร้างคำขอ http ด้วยตัวเราเองเราต้องพูดถึงประเภทเนื้อหาใช่ไหม
GMsoF

2
@GMsoF มันเป็นตัวเลือก ดูstackoverflow.com/a/16693884/632951 คุณอาจต้องการหลีกเลี่ยงการใช้ชนิดเนื้อหาเมื่อสร้างคำร้องขอเฉพาะสำหรับเซิร์ฟเวอร์เฉพาะเพื่อหลีกเลี่ยงค่าใช้จ่ายทั่วไป
Pacerier

2

เพียงเล็กน้อยจากด้านข้างของฉันสำหรับการอัปโหลดข้อมูลภาพผ้าใบ HTML5:

ฉันกำลังทำงานในโครงการสำหรับโรงพิมพ์และมีปัญหาเนื่องจากการอัปโหลดภาพไปยังเซิร์ฟเวอร์ที่มาจากcanvasองค์ประกอบHTML5 ฉันพยายามอย่างน้อยหนึ่งชั่วโมงและฉันไม่ได้รับมันเพื่อบันทึกภาพอย่างถูกต้องบนเซิร์ฟเวอร์ของฉัน

เมื่อฉันตั้งค่า contentTypeตัวเลือกการโทร jQuery ajax ของฉันให้application/x-www-form-urlencodedทุกอย่างเป็นไปอย่างถูกต้องและข้อมูลที่เข้ารหัสด้วยรหัส 64 ถูกตีความอย่างถูกต้องและบันทึกเป็นรูปภาพได้สำเร็จ


บางทีนั่นอาจช่วยให้ใครบางคน!


4
มีการส่งเนื้อหาประเภทใดก่อนที่คุณจะเปลี่ยน ปัญหานี้อาจเกิดจากเซิร์ฟเวอร์ไม่รองรับประเภทเนื้อหาที่คุณส่งเป็น
catorda

1

หากคุณต้องการใช้ Content-Type = x-www-urlencoded-form อย่าใช้ FormDataCollection เป็นพารามิเตอร์: ใน asp.net Core 2+ FormDataCollection ไม่มีตัวสร้างเริ่มต้นที่ Formatters ต้องการ ใช้ IFormCollection แทน:

 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.