W HTTP istnieją dwa sposoby wysyłania danych POST: application/x-www-form-urlencoded
i multipart/form-data
. Rozumiem, że większość przeglądarek może przesyłać pliki tylko wtedy, gdy multipart/form-data
jest używana. Czy są jakieś dodatkowe wskazówki, kiedy używać jednego z typów kodowania w kontekście interfejsu API (bez przeglądarki)? Może to być na przykład oparte na:
- rozmiar danych
- istnienie znaków spoza ASCII
- istnienie na (niekodowanych) danych binarnych
- potrzeba przesłania dodatkowych danych (takich jak nazwa pliku)
Zasadniczo nie znalazłem w Internecie żadnych formalnych wskazówek dotyczących korzystania z różnych typów treści.
http
post
http-headers
max
źródło
źródło
Odpowiedzi:
TL; DR
Podsumowanie; jeśli masz dane binarne (nie alfanumeryczne) (lub ładunek o znacznej wielkości) do przesłania, użyj
multipart/form-data
. W przeciwnym razie użyjapplication/x-www-form-urlencoded
.Wspomniane typy MIME to dwa
Content-Type
nagłówki żądań HTTP POST, które muszą obsługiwać programy klienckie (przeglądarki). Celem obu tych typów żądań jest przesłanie listy par nazwa / wartość na serwer. W zależności od rodzaju i ilości przesyłanych danych jedna z metod będzie wydajniejsza od drugiej. Aby zrozumieć, dlaczego, musisz spojrzeć na to, co każdy robi pod przykryciem.Ponieważ
application/x-www-form-urlencoded
treść wiadomości HTTP wysyłanej do serwera to zasadniczo jeden gigantyczny ciąg zapytania - pary nazwa / wartość są oddzielone znakiem ampersand (&
), a nazwy są oddzielone od wartości symbolem równości (=
). Przykładem tego może być:MyVariableOne=ValueOne&MyVariableTwo=ValueTwo
Zgodnie ze specyfikacją :
Oznacza to, że dla każdego niealfanumerycznego bajtu, który istnieje w jednej z naszych wartości, jego reprezentacja zajmie trzy bajty. W przypadku dużych plików binarnych potrojenie ładunku będzie wysoce nieefektywne.
Właśnie tam
multipart/form-data
. W tej metodzie przesyłania par nazwa / wartość każda para jest reprezentowana jako „część” komunikatu MIME (jak opisano w innych odpowiedziach). Części są oddzielone określoną granicą łańcucha (wybraną specjalnie, aby ten łańcuch granicy nie występował w żadnym z ładunków „wartościowych”). Każda część ma swój własny zestaw nagłówków MIME, takich jakContent-Type
, a zwłaszczaContent-Disposition
, które mogą nadać każdej części jej „nazwę”. Element wartości każdej pary nazwa / wartość stanowi ładunek każdej części komunikatu MIME. Specyfikacja MIME daje nam więcej opcji przy reprezentowaniu ładunku wartości - możemy wybrać bardziej wydajne kodowanie danych binarnych w celu zaoszczędzenia przepustowości (np. Base 64 lub nawet surowe pliki binarne).Dlaczego nie korzystać
multipart/form-data
cały czas? W przypadku krótkich wartości alfanumerycznych (jak większość formularzy internetowych) narzut związany z dodaniem wszystkich nagłówków MIME znacznie przewyższy wszelkie oszczędności wynikające z bardziej wydajnego kodowania binarnego.źródło
CZYTAJ CO NAJMNIEJ PIERWSZĄ PARĘ TUTAJ!
Wiem, że to 3 lata za późno, ale odpowiedź Matta (zaakceptowana) jest niekompletna i ostatecznie wpakuje cię w kłopoty. Kluczem tutaj jest to, że jeśli zdecydujesz się użyć
multipart/form-data
, granica nie może pojawić się w danych pliku, które serwer ostatecznie otrzyma.Nie stanowi to problemu
application/x-www-form-urlencoded
, ponieważ nie ma granicy.x-www-form-urlencoded
może również zawsze obsługiwać dane binarne, po prostu zamieniając jeden dowolny bajt na trzy7BIT
bajty. Nieefektywne, ale działa (i zauważ, że komentarz o niemożności wysyłania nazw plików oraz danych binarnych jest niepoprawny; po prostu wysyłasz go jako inną parę klucz / wartość).Problem
multipart/form-data
polega na tym, że separator granic nie może być obecny w danych pliku (patrz RFC 2388 ; sekcja 5.2 zawiera również dość kiepską wymówkę, że nie ma odpowiedniego zagregowanego typu MIME, który pozwala uniknąć tego problemu).Tak, na pierwszy rzut oka,
multipart/form-data
nie ma żadnej wartości w ogóle w jakikolwiek przesłanie pliku, binarny lub inaczej. Jeśli nie wybierzesz poprawnie granicy, w końcu będziesz mieć problem, niezależnie od tego, czy wysyłasz zwykły tekst, czy surowy plik binarny - serwer znajdzie granicę w niewłaściwym miejscu, a plik zostanie obcięty lub test POST zawiedzie.Kluczem jest wybranie kodowania i granicy, tak aby wybrane znaki graniczne nie mogły pojawić się w zakodowanym wyjściu. Jednym prostym rozwiązaniem jest użycie
base64
( nie używaj surowego pliku binarnego). W base64 3 dowolne bajty są zakodowane w czterech 7-bitowych znakach, gdzie wyjściowy zestaw znaków to[A-Za-z0-9+/=]
(tj. Alfanumeryczny, „+”, „/” lub „=”).=
jest szczególnym przypadkiem i może pojawić się tylko na końcu zakodowanego wyjścia jako pojedynczy=
lub podwójny==
. Teraz wybierz swoją granicę jako 7-bitowy ciąg ASCII, który nie może pojawić się nabase64
wyjściu. Wiele wyborów, które widzisz w sieci, nie spełnia tego testu - MDN tworzy dokumenty, na przykład użyj „obiektu blob” jako granicy podczas wysyłania danych binarnych - źle. Jednak coś takiego jak „! Blob!” nigdy nie pojawi się nabase64
wyjściu.źródło
index === -1
.'()+-./:=
wtedy użycie . Jeszcze losowe generowanie podciąg z wyboru jest jeszcze do zrobienia i można to zrobić z jednej linii:while(true){r = rand(); if(data.indexOf(r) === -1){doStuff();break;}}
. Sugestia EML (konwersja do base64, aby uniknąć dopasowania podciągów) jest po prostu dziwna, nie wspominając o tym, że wiąże się z niepotrzebnym spadkiem wydajności. I wszystkie kłopoty za nic, ponieważ algorytm jednowierszowy jest równie prosty i prosty. Base64 nie powinien być (ab) używany w ten sposób, ponieważ treść HTTP akceptuje wszystkie 8-bitowe oktety.Nie sądzę, że HTTP jest ograniczony do POST w postaci wieloczęściowej lub w formie x-www-form-urlencoded. Nagłówka Content-Type jest prostopadła do metody HTTP POST (można wypełnić typ odpowiedni dla ciebie MIME). Dzieje się tak również w przypadku typowych aplikacji webowych opartych na reprezentacji HTML (np. Ładunek json stał się bardzo popularny do przesyłania ładunku dla żądań ajax).
Jeśli chodzi o Restful API przez HTTP, najpopularniejszymi typami treści, z którymi się skontaktowałem, są application / xml i application / json.
application / xml:
application / json
dane binarne jako zasoby własne
Próbowałbym przedstawić dane binarne jako własny zasób / zasób. Dodaje kolejne połączenie, ale lepiej oddziela rzeczy. Przykładowe obrazy:
W późniejszych zasobach możesz po prostu wstawić zasób binarny jako link:
źródło
Zgadzam się z tym, co powiedział Manuel. W rzeczywistości jego komentarze odnoszą się do tego adresu URL ...
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4
... który stwierdza:
Jednak dla mnie sprowadzałoby się to do obsługi narzędzi / ram.
Jeśli uzyskasz jasny obraz użytkowników i tego, w jaki sposób będą korzystać z Twojego interfejsu API, pomoże Ci to podjąć decyzję. Jeśli utrudnisz przesyłanie plików użytkownikom interfejsu API, to oni się odejdą, poświęcając dużo czasu na ich obsługę.
Drugi to wsparcie narzędzi, które masz do pisania API i jak łatwo jest dostosować jeden mechanizm przesyłania do drugiego.
źródło
Mała wskazówka z mojej strony dotycząca przesyłania danych obrazu płótna HTML5:
Pracuję nad projektem dla drukarni i miałem problemy z przesyłaniem obrazów na serwer, które pochodziły z
canvas
elementu HTML5 . Walczyłem przez co najmniej godzinę i nie udało mi się go poprawnie zapisać obrazu na moim serwerze.Po ustawieniu
contentType
opcji mojego wywołania jQuery ajaxapplication/x-www-form-urlencoded
wszystko poszło we właściwy sposób, a dane zakodowane w base64 zostały poprawnie zinterpretowane i pomyślnie zapisane jako obraz.Może to komuś pomaga!
źródło
Jeśli potrzebujesz użyć Content-Type = x-www-urlencoded-form, NIE używaj FormDataCollection jako parametru: W asp.net Core 2+ FormDataCollection nie ma domyślnych konstruktorów wymaganych przez Formatters. Zamiast tego użyj IFormCollection:
źródło