Как опубликовать данные формы с помощью fetch api?

118

Мой код:

fetch("api/xxx", {
    body: new FormData(document.getElementById("form")),
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
        // "Content-Type": "multipart/form-data",
    },
    method: "post",
}

Я попытался опубликовать свою форму с помощью fetch api, и тело, которое он отправляет, выглядит так:

-----------------------------114782935826962
Content-Disposition: form-data; name="email"

test@example.com
-----------------------------114782935826962
Content-Disposition: form-data; name="password"

pw
-----------------------------114782935826962--

(Я не знаю, почему число в границе меняется каждый раз при отправке ...)

Я хочу, чтобы он отправлял данные с «Content-Type»: «application / x-www-form-urlencoded», что мне делать? Или, если мне просто нужно с этим справиться, как мне декодировать данные в моем контроллере?


Кому ответьте на мой вопрос, я знаю, что могу сделать это с помощью:

fetch("api/xxx", {
    body: "email=test@example.com&password=pw",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
    },
    method: "post",
}

Я хочу что-то вроде $ ("# form"). Serialize () в jQuery (без использования jQuery) или способ декодирования mulitpart / form-data в контроллере. Спасибо за ответы.

Зак
источник
В чем проблема с использованием FormData?
guest271314
1
Я хочу опубликовать его как "email=test@example.com&password=pw". Является ли это возможным?
Zack
1
«Я не знаю, почему номер в границе меняется каждый раз, когда он отправляет…» - Идентификатор границы - это просто случайный идентификатор, он может быть любым и сам по себе не имеет никакого значения. Так что нет ничего плохого в том, чтобы выбрать здесь случайное число (что обычно и делают клиенты).
совать

Ответы:

149

Процитирую MDNFormData (выделено мной):

FormDataИнтерфейс обеспечивает возможность легко построить набор пар ключ / значение , представляющих поля формы и их значения, которые затем могут быть легко посланные с использованием XMLHttpRequest.send()метода. Он использует тот же формат, что и форма, если бы был установлен тип кодировки"multipart/form-data" .

Поэтому при использовании FormDataвы запираетесь в multipart/form-data. Невозможно отправить FormDataобъект как тело и не отправлять данные в multipart/form-dataформате.

Если вы хотите отправить данные, application/x-www-form-urlencodedвам нужно будет либо указать тело как строку в кодировке URL, либо передать URLSearchParamsобъект. К сожалению, последний не может быть инициализирован напрямую из formэлемента. Если вы не хотите самостоятельно перебирать элементы формы (что вы могли бы сделать с помощью HTMLFormElement.elements), вы также можете создать URLSearchParamsобъект из FormDataобъекта:

const data = new URLSearchParams();
for (const pair of new FormData(formElement)) {
    data.append(pair[0], pair[1]);
}

fetch(url, {
    method: 'post',
    body: data,
})
.then(…);

Обратите внимание, что вам не нужно указывать Content-Typeзаголовок самостоятельно.


Как отметил monk-time в комментариях, вы также можете создать URLSearchParamsи передать FormDataобъект напрямую, вместо добавления значений в цикл:

const data = new URLSearchParams(new FormData(formElement));

Тем не менее, это все еще имеет экспериментальную поддержку в браузерах, поэтому обязательно проверьте это перед использованием.

тыкать
источник
18
Вы также можете использовать объект или просто FormDataв конструкторе напрямую вместо цикла:new URLSearchParams(new FormData(formElement))
monk-time
@ monk-time На момент написания этого ответа аргумент конструктора для URLSearchParamsбыл очень новым и имел очень ограниченную поддержку.
poke
3
извините, это не была жалоба, просто примечание для всех, кто прочтет это в будущем.
monk-time
1
@Prasanth Вы можете сами явно указать тип контента, но вы должны выбрать правильный . Легче просто оставить это и fetchпозаботиться об этом за вас.
poke
1
@chovy URLSearchParamsвстроен в большинство современных браузеров как глобальный объект, а также работает из Node.
тыкают
68

Клиент

Не устанавливайте заголовок типа содержимого.

// Build formData object.
let formData = new FormData();
formData.append('name', 'John');
formData.append('password', 'John123');

fetch("api/SampleData",
    {
        body: formData,
        method: "post"
    });

Сервер

Используйте FromFormатрибут, чтобы указать, что источником привязки являются данные формы.

[Route("api/[controller]")]
public class SampleDataController : Controller
{
    [HttpPost]
    public IActionResult Create([FromForm]UserDto dto)
    {
        return Ok();
    }
}

public class UserDto
{
    public string Name { get; set; }
    public string Password { get; set; }
}
Regnauld
источник
4
Хотя это работает, это не отправляет данные, application/x-www-form-urlencodedкоторые запрашивает OP.
совать
5
Для меня это сработало, когда я УДАЛЯЛ Content-Type из шапки и позволяю браузеру делать это автоматически. Спасибо!
Крис
Спасибо, @regnauld весь день пытался решить эту проблему!
ak85
1
Если вы не установите Content-type для Fetch, он будет установлен как multipart/form-data, что и должно быть для данных формы! Затем вы можете использовать multerв expressjs для простого анализа этого формата данных.
kyw
23

Вы можете установить bodyэкземпляр URLSearchParamsсо строкой запроса, переданной в качестве аргумента

fetch("/path/to/server", {
  method:"POST"
, body:new URLSearchParams("email=test@example.com&password=pw")
})

document.forms[0].onsubmit = async(e) => {
  e.preventDefault();
  const params = new URLSearchParams([...new FormData(e.target).entries()]);
  // fetch("/path/to/server", {method:"POST", body:params})
  const response = await new Response(params).text();
  console.log(response);
}
<form>
  <input name="email" value="test@example.com">
  <input name="password" value="pw">
  <input type="submit">
</form>

гость271314
источник
2
Reflect.apply(params.set, params, props)это особенно нечитаемый способ сказать params.set(props[0], props[1]).
совать
@poke Reflect.apply(params.set, params, props)здесь читается с точки зрения перспективы.
guest271314,
Кажется, это единственный рабочий ответ здесь: / спасибо! :)
OZZIE
0

Используйте FormDataи, fetchчтобы получать и отправлять данные

Камил Келчевски
источник
0
function card(fileUri) {
let body = new FormData();
let formData = new FormData();
formData.append('file', fileUri);

fetch("http://X.X.X.X:PORT/upload",
  {
      body: formData,
      method: "post"
  });
 }
суфиан Эламмари
источник
7
Ответы только на код обычно можно улучшить, добавив объяснения того, как и почему они работают. Добавляя ответ на вопрос двухлетней давности к существующим ответам, важно указать, какой новый аспект вопроса касается вашего ответа.
Джейсон Аллер,