application / x-www-form-urlencoded veya multiart / form-data?


1335

HTTP'de veriyi POST etmenin iki yolu vardır: application/x-www-form-urlencodedve multipart/form-data. Çoğu tarayıcının yalnızca multipart/form-datakullanılırsa dosya yükleyebildiğini anlıyorum . API bağlamında kodlama türlerinden birinin ne zaman kullanılacağı konusunda ek bir rehber var mı (tarayıcı dahil değil)? Bu, örneğin aşağıdakilere dayalı olabilir:

  • veri boyutu
  • ASCII olmayan karakterlerin varlığı
  • (kodlanmamış) ikili verilerin varlığı
  • ek veri aktarma ihtiyacı (dosya adı gibi)

Temelde web'de şimdiye kadar farklı içerik türlerinin kullanımı konusunda resmi bir rehberlik bulamadım.


74
Bunların HTML formlarının kullandığı iki MIME türü olduğu belirtilmelidir. HTTP'nin böyle bir sınırlaması yoktur ... HTTP aracılığıyla istediği MIME türünü kullanabilirsiniz.
tybro0103

Yanıtlar:


2013

TL; DR

Özet; iletilecek ikili (alfasayısal olmayan) verileriniz (veya önemli ölçüde boyutlandırılmış bir yükünüz) varsa kullanın multipart/form-data. Aksi takdirde kullanın application/x-www-form-urlencoded.


Bahsettiğiniz MIME türleri Content-Type, kullanıcı aracılarının (tarayıcıların) desteklemesi gereken HTTP POST istekleri için iki başlıktır. Bu tür isteklerin her ikisinin de amacı, sunucuya bir ad / değer çifti listesi göndermektir. İletilen verilerin türüne ve miktarına bağlı olarak, yöntemlerden biri diğerinden daha verimli olacaktır. Nedenini anlamak için, her birinin kapakların altında ne yaptığını incelemelisiniz.

Çünkü application/x-www-form-urlencoded, sunucuya gönderilen HTTP iletisinin gövdesi esasen bir dev sorgu dizesidir - ad / değer çiftleri ve işareti ( &) ile ayrılır ve adlar eşittir simgesiyle ( =) değerlerden ayrılır . Bunun bir örneği: 

MyVariableOne=ValueOne&MyVariableTwo=ValueTwo

Şartnameye göre :

[Ayrılmış ve alfasayısal olmayan karakterlerin yerine “% HH”, yüzde işareti ve karakterin ASCII kodunu temsil eden iki onaltılık basamak gelir

Bu, değerlerimizden birinde bulunan her alfasayısal olmayan bayt için, onu temsil etmek için üç bayt süreceği anlamına gelir. Büyük ikili dosyalar için, yükün üçe katlanması oldukça verimsiz olacaktır.

İşte bu noktada multipart/form-datadevreye girer. Bu isim / değer çiftlerini iletme yöntemiyle, her bir çift MIME mesajında ​​(diğer cevaplarda açıklandığı gibi) bir "parça" olarak temsil edilir. Parçalar belirli bir dize sınırıyla ayrılır (özellikle bu sınır dize "değer" yüklerinden hiçbirinde oluşmayacak şekilde seçilir). Her parçanın kendi MIME üstbilgileri kümesi vardır Content-Typeve özellikle Content-Dispositionher bölüme "adını" verebilir. Her bir ad / değer çiftinin değer parçası, MIME mesajının her bir bölümünün yüküdür. MIME spesifikasyonu, değer yükünü temsil ederken bize daha fazla seçenek sunar - bant genişliğinden tasarruf etmek için ikili verilerin daha verimli bir kodlamasını seçebiliriz (örneğin, temel 64 veya hatta ham ikili).

Neden sürekli kullanmıyorsunuz multipart/form-data? Kısa alfanümerik değerler için (çoğu web formu gibi), tüm MIME başlıklarının eklenmesi, daha verimli ikili kodlamadan elde edilen tasarruflardan önemli ölçüde daha ağır basacaktır.


84
X-www-form-urlencoded bir uzunluk sınırına sahip mi, yoksa sınırsız mı?
Pacerier

34
@Pacerier Sınır, POST isteğini alan sunucu tarafından uygulanır. Daha fazla tartışma için bu konuya bakın: stackoverflow.com/questions/2364840/…
Matt Bridges

5
@ZiggyTheHamster JSON ve BSON'un her biri farklı veri türleri için daha verimlidir. Base64, her iki serileştirme yöntemi için gzip'ten daha düşüktür. Base64 herhangi bir avantaj sağlamaz, HTTP ikili pyload'ları destekler.
Tiberiu-Ionuț Stan

16
Ayrıca, bir form adlandırılmış bir dosya yüklemesi içeriyorsa, tek seçeneğiniz form verisidir, çünkü urlencoded'in dosya adını yerleştirmenin bir yolu yoktur (form verilerinde içerik düzenine ad parametresidir).
Guido van Rossum

4
@EML parantezimi gör "(bu sınır dizesinin" değer "yüklerinden hiçbirinde oluşmaması için özel olarak seçilmiştir)"
Matt Bridges

151

İLK PARA BURADA EN AZ OKUYUN!

Bunun 3 yıl çok geç olduğunu biliyorum, ama Matt'in (kabul edilen) cevabı eksik ve sonunda başını belaya sokacak. Anahtar burada kullanmayı seçerseniz, yani multipart/form-data, sınır zorunluluk değil sunucu sonunda aldığı dosya, veri görünümünde.

Bu bir problem değil application/x-www-form-urlencoded, çünkü sınır yok. x-www-form-urlencodedayrıca, rastgele bir baytı üç 7BITbayta dönüştürmenin basit bir yolu ile her zaman ikili verileri işleyebilir . Verimsiz, ancak işe yarıyor (ve dosya adlarını ve ikili verileri gönderememe hakkındaki yorumun yanlış olduğunu unutmayın; sadece başka bir anahtar / değer çifti olarak gönderirsiniz).

Sorun multipart/form-data, sınır ayırıcısının dosya verilerinde bulunmaması gerektiğidir (bkz. RFC 2388 ; bölüm 5.2, bu sorunu önleyen uygun bir toplu MIME türüne sahip olmamak için oldukça topal bir bahane içerir).

Yani, ilk bakışta, herhangi bir dosya yüklemesinde, ikili veya başka bir şekilde multipart/form-datahiçbir değeri yoktur . Doğru şekilde sınır seçmezseniz, o zaman olacak sunucu yanlış yerde bir sınır bulacaksınız ve dosyanızın kesilecek, veya POST - Sonunda düz metin veya ham ikili gönderiyorsanız olsun, bir sorun var başaramayacak.

Anahtar, seçtiğiniz sınır karakterleri kodlanmış çıktıda görünmeyecek şekilde bir kodlama ve sınır seçmektir. Bir basit bir çözüm kullanımına olan base64(do not ham ikili kullanın). Gelen base64 3 rasgele bayt çıkış karakter kümesi dört adet 7-bit karakter, kodlanmaktadır [A-Za-z0-9+/=](yani alfasayısal, '+', '/' veya '='). =özel bir durumdur ve yalnızca kodlanmış çıktının sonunda tek =veya çift olarak görünebilir ==. Şimdi sınırınızı base64çıktıda görünmeyen 7 bitlik ASCII dizesi olarak seçin . İnternette gördüğünüz birçok seçenek bu testte başarısız oluyor - MDN dokümanlar oluşturuyor, örneğin, ikili verileri gönderirken bir sınır olarak "blob" kullanın - iyi değil. Ancak, "! Blob!" base64çıkışta asla görünmez .


52
Çok parçalı / form verilerinin göz önünde bulundurulması, sınırın verilerde görünmemesini sağlamakla birlikte, bu, yeterince uzun bir sınır seçerek başarılması oldukça kolaydır. Bunu yapmak için lütfen base64 kodlamasını kullanmayın. Rastgele oluşturulan ve UUID ile aynı uzunlukta bir sınır yeterli olmalıdır: stackoverflow.com/questions/1705008/… .
Joshcodes

20
@EML, Bu hiç mantıklı değil. Açıkçası, sınır http istemcisi (tarayıcı) tarafından otomatik olarak seçilir ve istemci, yüklenen dosyalarınızın içeriğiyle çakışan bir sınır kullanmayacak kadar akıllı olacaktır. Bu basit bir alt dize eşleşmesi gibi index === -1.
Pacerier

13
@Pacerier: (A) şu soruyu okudu: "tarayıcı yok, API içeriği". (B) tarayıcılar zaten sizin için istek oluşturmuyor. Elle kendiniz yaparsınız. Tarayıcılarda sihir yok.
EML

12
@BeniBela, muhtemelen '()+-./:=o zaman kullanmayı önerecek . Oysa çek alt dize ile rastgele nesil hala gitmek için yoludur ve bir satır ile yapılabilir: while(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}. EML'nin önerisi (sadece alt dizelerin eşleşmesini önlemek için base64'e dönüştürün) sadece gariptir, gereksiz performans düşüşüyle ​​birlikte bahsetmiyoruz. Ve tek satır algoritması eşit derecede basit ve basit olduğundan, hiçbir şey için tüm sorun. HTTP gövdesi 8 bit oktetlerin tümünü kabul ettiğinden, Base64 (ab) bu ​​şekilde kullanılmaz .
Pacerier

31
Bu cevap sadece tartışmaya bir şey katmakla kalmaz, aynı zamanda yanlış tavsiyeler de verir. Birincisi, rastgele veriler ayrı parçalar halinde iletildiğinde, seçilen sınırın faydalı yükte mevcut olması her zaman mümkündür. Bunun olmadığından emin olmanın tek yolu, ortaya koyduğumuz her sınır için tüm yükü incelemektir. Tamamen pratik değil. Sadece bir çarpışmanın sınırsız olasılığını kabul ediyoruz ve "--- sınır- <burada UUID> -boyut -" gibi makul bir sınır buluyoruz. İkincisi, her zaman Base64 kullanmak bant genişliğini boşa harcar ve hiçbir sebep olmadan arabellekleri doldurur.
vagelis

92

Ben HTTP çok parçalı veya x-www-form-urlencoded POST ile sınırlı olduğunu sanmıyorum. Content-Type Başlık HTTP POST metoduna dik olan (hangi takım elbise sen MIME türü doldurabilirsiniz). Bu aynı zamanda tipik HTML temsili tabanlı webapps için de geçerlidir (örn. Json yükü, ajax istekleri için yükün iletilmesinde çok popüler hale geldi).

HTTP üzerinden Restful API ile ilgili olarak iletişim kurduğum en popüler içerik türleri application / xml ve application / json'dur.

application / xml:

  • veri boyutu: XML çok ayrıntılı, ancak sıkıştırma kullanırken ve yazma erişim durumunun (örneğin POST veya PUT aracılığıyla) okuma erişimi olarak çok daha nadir olduğunu düşünürken genellikle sorun değil (çoğu durumda tüm trafiğin <% 3'üdür) ). Yazma performansını optimize etmek zorunda kaldığım durumlar nadiren orada
  • ascii olmayan karakterlerin varlığı: utf-8'i XML'de kodlama olarak kullanabilirsiniz
  • ikili verilerin varlığı: base64 kodlaması kullanmanız gerekir
  • dosyaadı verileri: XML içindeki bu iç alanı kapsülleyebilirsiniz

uygulama / json

  • veri boyutu: XML'den daha kompakt, hareketsiz metin, ancak sıkıştırabilirsiniz
  • ascii olmayan karakterler: json utf-8
  • ikili veri: base64 (ayrıca bkz. json-binary-question )
  • dosya adı verileri: json içinde kendi alan bölümü olarak kapsülleme

kendi verileri olarak ikili veriler

İkili verileri kendi varlık / kaynak olarak göstermeye çalışırdım. Başka bir çağrı ekler ama daha iyi şeyler ayrıştırır. Örnek görüntüler:

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

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

Daha sonraki kaynaklarda ikili kaynağı sadece bağlantı olarak satır içine alabilirsiniz:

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

İlginç. Fakat ne zaman application / x-www-form-urlencoded kullanılır ve ne zaman multipart / form-data?
en fazla

3
application / x-www-form-urlencoded, isteğinizin varsayılan mime tipidir (ayrıca bkz . w3.org/TR/html401/interact/forms.html#h-17.13.4 ). Bunu "normal" web formları için kullanıyorum. API için application / xml | json kullanıyorum. multipart / form-data eklerin düşünülmesinde bir zildir (yanıt gövdesinde birkaç veri bölümü tanımlanmış bir sınır dizesiyle birleştirilir).
manuel aldana

4
Bence OP muhtemelen sadece HTML formlarının kullandığı iki tür hakkında soruyordu, ama bunun işaret edildiğine sevindim.
tybro0103

30

Manuel'in söylediğine çok katılıyorum. Aslında, yorumları bu url'ye atıfta bulunuyor ...

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

... belirttiği:

"Application / x-www-form-urlencoded" içerik türü, büyük miktarlarda ikili veri veya ASCII olmayan karakterler içeren metin göndermek için yetersizdir. "Çok bölümlü / form verileri" içerik türü, dosyalar, ASCII olmayan veriler ve ikili veriler içeren formlar göndermek için kullanılmalıdır.

Ancak, benim için araç / çerçeve desteğine gelirdi.

  • API kullanıcılarınızın uygulamalarını hangi araçlarla oluşturmasını bekliyorsunuz?
  • Bir yöntemi diğerine tercih edebilecekleri çerçeveleri veya bileşenleri var mı?

Kullanıcılarınız ve API'nizi nasıl kullanacakları hakkında net bir fikir edinebilirsiniz, bu karar vermenize yardımcı olacaktır. API kullanıcılarınız için dosyaların yüklenmesini zorlaştırırsanız, bu dosyalar taşınır, bunları desteklemek için çok zaman harcarsınız.

Buna ikincil olarak, API'nizi yazmak için SİZİN araç desteği ve bir yükleme mekanizmasını diğerine yerleştirmenin ne kadar kolay olduğu olacaktır.


1
Merhaba, bu her şeyi web sunucusuna her gönderdiğimizde, web sunucusunun verileri deşifre etmesi gerektiğini bildirmek için İçerik türünün ne olduğunu belirtmemiz gerektiği anlamına mı geliyor? Http isteğini kendimiz oluştursak bile, İçerik türünden bahsetmeliyiz değil mi?
GMsoF

2
@ GMsoF, isteğe bağlıdır. Bkz. Stackoverflow.com/a/16693884/632951 . Genel ek yüklerden kaçınmak için belirli bir sunucu için belirli bir istek hazırlarken içerik türü kullanmaktan kaçınmak isteyebilirsiniz.
Pacerier

2

HTML5 tuval görüntü verilerini yüklemek için yanımdan sadece küçük bir ipucu:

Bir matbaa için bir proje üzerinde çalışıyorum ve HTML5 canvasöğesinden gelen sunucuya resim yüklerken bazı sorunlar yaşadım . En az bir saat uğraşıyordum ve görüntüyü sunucuma doğru şekilde kaydetmeyi alamadım.

Bir kez contentTypejQuery ajax çağrım seçeneğini application/x-www-form-urlencodedher şeye doğru şekilde ayarladım ve base64 kodlu veriler doğru bir şekilde yorumlandı ve bir görüntü olarak başarıyla kaydedildi.


Belki birine yardım eder!


4
Siz değiştirmeden önce hangi içerik türünü gönderiyor? Bu sorun, sunucunun, gönderdiğiniz içerik türünü desteklememesinden kaynaklanıyor olabilir.
catorda

1

Content-Type = x-www-urlencoded-form kullanmanız gerekiyorsa, FormDataCollection parametresini parametre olarak KULLANMAYIN: asp.net Core 2+ uygulamasında FormDataCollection öğesinin Formatters tarafından gerekli olan varsayılan kurucuları yoktur. Bunun yerine IFormCollection kullanın:

 public IActionResult Search([FromForm]IFormCollection type)
    {
        return Ok();
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.