получить сообщение с данными из нескольких частей

86

Я получаю такой URL-адрес:

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)
})

Мой API ожидает, что данные будут, multipart/form-dataпоэтому я использую content-typeэтот тип ... Но он дает мне ответ с кодом состояния 400.

Что не так с моим кодом?

ариец
источник

Ответы:

163

Вы устанавливаете значение Content-Typebe multipart/form-data, но затем используете JSON.stringifyданные тела, которые возвращаются application/json. У вас несоответствие типа контента.

Вам нужно будет кодировать свои данные как multipart/form-dataвместо json. Обычно multipart/form-dataиспользуется при загрузке файлов и немного сложнее application/x-www-form-urlencoded(по умолчанию для HTML-форм).

Спецификацию multipart/form-dataможно найти в RFC 1867 .

Руководство о том, как отправлять такие данные с помощью javascript, см. Здесь .

Основная идея - использовать объект FormData (не поддерживается в 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
  });

  // ...
}

За этой статьи убедитесь , что не установить Content-Typeзаголовок. Браузер установит его за вас, включая boundaryпараметр.

Россопедия
источник
const fd = new FormData (); // Файл для загрузки. fd.append ('файл', fileToUpload); fd.append ('jsondatakey', 'jsondatavalue'); Благодаря этому вы сможете отправить файл вместе с некоторыми данными json в теле.
Джнана
25

Недавно я работал с IPFS и решил это. Пример curl для IPFS для загрузки файла выглядит так:

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"

Основная идея заключается в том, что каждая часть (разбивка по строке в boundaryс --) имеет свои собственные заголовки ( Content-Typeво второй части, например.)FormData Объект управляет всем этим для вас, так что это лучший способ для достижения наших целей.

Это переводится как получение API следующим образом:

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)
})
консумер
источник
16
Обратите внимание на вышеупомянутый метод. НЕ предоставляйте заголовки, если вы делаете это с использованием FormData, потому что он переопределит автоматически установленную границу.
Мэтт Пенджелли
1
Спасибо @MattPengelly! Как тогда установить собственные заголовки, такие как авторизация?
Dragos
7
@DragosStrugar, вы все равно можете устанавливать заголовки (включая авторизацию), просто не устанавливайте вручную заголовок Content-Type, если вы используете FormData.
RobertMcReed 01
2
НЕ поставляйте заголовки с Content-Type, если он использует FormData.
caot
1
В примере с завитком он вам нужен. В этом FormDataпримере вам это не нужно, потому что браузер отправляет этот заголовок за вас, а также управляет всеми границами mime, что и является целью этого решения.
konsumer