pobierz post z danymi formularza wieloczęściowego

86

Pobieram adres URL taki jak ten:

fetch(url, {
  mode: 'no-cors',
  method: method || null,
  headers: {
    'Accept': 'application/json, application/xml, text/plain, text/html, *.*',
    'Content-Type': 'multipart/form-data'
  },
  body: JSON.stringify(data) || null,
}).then(function(response) {
  console.log(response.status)
  console.log("response");
  console.log(response)
})

Moje API oczekuje, że dane będą multipart/form-datatakie, więc używam content-typetego typu ... Ale daje mi odpowiedź z kodem statusu 400.

Co jest nie tak z moim kodem?

aryjski
źródło

Odpowiedzi:

163

Ustawiasz Content-Typebyć multipart/form-data, ale następnie używasz JSON.stringifydanych ciała, które zwracają application/json. Masz niezgodny typ zawartości.

Będziesz musiał zakodować swoje dane jako multipart/form-datazamiast json. Zwykle multipart/form-datajest używany podczas przesyłania plików i jest nieco bardziej skomplikowany niż application/x-www-form-urlencoded(co jest domyślne w przypadku formularzy HTML).

Specyfikację multipart/form-datamożna znaleźć w RFC 1867 .

Aby uzyskać przewodnik na temat przesyłania tego rodzaju danych za pomocą JavaScript, zobacz tutaj .

Podstawowym pomysłem jest użycie obiektu FormData (nieobsługiwanego w IE <10):

async function sendData(url, data) {
  const formData  = new FormData();

  for(const name in data) {
    formData.append(name, data[name]);
  }

  const response = await fetch(url, {
    method: 'POST',
    body: formData
  });

  // ...
}

Zgodnie z tym artykułem pamiętaj, aby nie ustawiać Content-Typenagłówka. Przeglądarka ustawi to za Ciebie, łącznie z boundaryparametrem.

rossipedia
źródło
const fd = new FormData (); // Plik do przesłania. fd.append ('plik', plikToUpload); fd.append ('jsondatakey', 'jsondatavalue'); Dzięki temu będziesz mógł wysłać plik wraz z niektórymi danymi json w treści.
Jnana
25

Niedawno pracowałem z IPFS i udało mi się to. Przykład curl dla IPFS do przesłania pliku wygląda następująco:

curl -i -H "Content-Type: multipart/form-data; boundary=CUSTOM" -d $'--CUSTOM\r\nContent-Type: multipart/octet-stream\r\nContent-Disposition: file; filename="test"\r\n\r\nHello World!\n--CUSTOM--' "http://localhost:5001/api/v0/add"

Podstawowym założeniem jest to, że każda część (rozszczepiony przez smyczkowy boundaryz --) ma własne nagłówki ( Content-Typew drugiej części, na przykład.) Do FormDataobiektu zarządza to wszystko dla Ciebie, więc jest to lepszy sposób, aby osiągnąć nasze cele.

Przekłada się to na pobieranie API w następujący sposób:

const formData = new FormData()
formData.append('blob', new Blob(['Hello World!\n']), 'test')

fetch('http://localhost:5001/api/v0/add', {
  method: 'POST',
  body: formData
})
.then(r => r.json())
.then(data => {
  console.log(data)
})
konsumer
źródło
16
Zwróć uwagę na powyższą metodę, NIE podawaj nagłówków, jeśli robisz to przy użyciu FormData, ponieważ spowoduje to przesłonięcie granicy ustawionej automatycznie.
Matt Pengelly,
1
Dzięki @MattPengelly! Jak więc ustawić niestandardowe nagłówki, takie jak Authorization?
Dragos Strugar,
7
@DragosStrugar nadal możesz ustawiać nagłówki (w tym autoryzacja), po prostu nie ustawiaj ręcznie nagłówka Content-Type, jeśli używasz FormData.
Robert McReed
2
NIE dostarczaj nagłówków z „Content-Type”, jeśli używa FormData.
caot
1
W przykładzie z lokami jest to potrzebne. W tym FormDataprzykładzie nie jest to potrzebne, ponieważ przeglądarka wysyła ten nagłówek za Ciebie, a także zarządza wszystkimi granicami mime, co jest celem tego rozwiązania.
konsumer