Como faço para postar dados de formulário com fetch api?

116

Meu código:

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

Tentei postar meu formulário usando fetch api, e o corpo que ele envia é assim:

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

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

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

(Não sei por que o número no limite é alterado toda vez que ele envia ...)

Desejo enviar os dados com "Content-Type": "application / x-www-form-urlencoded", o que devo fazer? Ou se eu apenas tenho que lidar com isso, como decodifico os dados no meu controlador?


Para quem responde à minha pergunta, eu sei que posso fazer isso com:

fetch("api/xxx", {
    body: "[email protected]&password=pw",
    headers: {
        "Content-Type": "application/x-www-form-urlencoded",
    },
    method: "post",
}

O que eu quero é algo como $ ("# form"). Serialize () em jQuery (sem usar jQuery) ou a forma de decodificar mulitpart / form-data no controlador. Obrigado por suas respostas.

Zack
fonte
Qual é o problema de usar FormData?
guest271314,
1
Quero postá-lo como "[email protected]&password=pw". É possível?
Zack,
1
“Eu não sei por que o número no limite é alterado toda vez que ele envia ...” - O identificador de limite é apenas um identificador aleatório, pode ser qualquer coisa e não tem nenhum significado por si só. Portanto, não há nada de errado em escolher um número aleatório ali (que é o que os clientes geralmente fazem).
toque em

Respostas:

148

Para citar MDN emFormData (grifo meu):

A FormDatainterface fornece uma maneira de construir facilmente um conjunto de pares de chave / valor representando campos de formulário e seus valores, que podem então ser facilmente enviados usando o XMLHttpRequest.send()método. Ele usa o mesmo formato que um formulário usaria se o tipo de codificação fosse definido como"multipart/form-data" .

Portanto, ao usar FormDatavocê está se bloqueando multipart/form-data. Não há como enviar um FormDataobjeto como corpo e não enviar dados no multipart/form-dataformato.

Se você quiser enviar os dados como application/x-www-form-urlencodedvocê terá que especificar o corpo como uma string codificada por URL ou passar um URLSearchParamsobjeto. O último infelizmente não pode ser inicializado diretamente de um formelemento. Se você não quiser iterar por meio dos elementos do formulário (o que pode ser feito usando HTMLFormElement.elements), também pode criar um URLSearchParamsobjeto a partir de um FormDataobjeto:

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

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

Observe que você não precisa especificar um Content-Typecabeçalho.


Conforme observado por monk-time nos comentários, você também pode criar URLSearchParamse passar o FormDataobjeto diretamente, em vez de anexar os valores em um loop:

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

Ele ainda tem algum suporte experimental em navegadores, então certifique-se de testar isso corretamente antes de usá-lo.

cutucar
fonte
18
Você também pode usar um objeto ou apenas FormDatano construtor diretamente em vez de um loop:new URLSearchParams(new FormData(formElement))
monk-time
@ monk-time No momento em que escrevi essa resposta, o argumento do construtor para URLSearchParamsera muito novo e tinha suporte muito limitado.
toque de
3
desculpe, isso não foi uma reclamação, apenas uma nota para todos que irão ler isso no futuro.
tempo monge de
1
@Prasanth Você mesmo pode especificar o tipo de conteúdo explicitamente, mas deve escolher o correto . É mais fácil simplesmente deixar de lado e fetchcuidar disso para você.
toque
1
@chovy URLSearchParamsé integrado à maioria dos navegadores modernos como um objeto global e também funciona a partir do Node.
cutucar
67

Cliente

Não defina o cabeçalho do tipo de conteúdo.

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

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

Servidor

Use o FromFormatributo para especificar que a origem da ligação são os dados do formulário.

[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
fonte
4
Enquanto isso funciona, isso não envia os dados, pois application/x-www-form-urlencodedé o que o OP está pedindo.
toque em
5
Para mim, funcionou quando REMOVA Content-Type do cabeçalho e deixava o navegador fazer isso automaticamente. Obrigado!
Chris
Obrigado, @regnauld tem tentado resolver isso o dia todo!
ak85 de
1
Se você não definir 'Content-type' para Fetch, será definido como multipart/form-data, que é o que deveria ser para dados de formulário! Depois, você pode usar o multerin expressjs para analisar esse formato de dados facilmente.
kyw
23

Você pode definir bodycomo uma instância de URLSearchParamscom string de consulta passada como argumento

fetch("/path/to/server", {
  method:"POST"
, body:new URLSearchParams("[email protected]&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="[email protected]">
  <input name="password" value="pw">
  <input type="submit">
</form>

guest271314
fonte
2
Reflect.apply(params.set, params, props)é uma forma particularmente ilegível de dizer params.set(props[0], props[1]).
toque em
@poke pode Reflect.apply(params.set, params, props)ser lido da perspectiva aqui.
guest271314,
Esta parece ser a única resposta que funciona aqui: / obrigado! :)
OZZIE
0

Use FormDatae fetchpara pegar e enviar dados

Kamil Kiełczewski
fonte
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"
  });
 }
soufiane ELAMMARI
fonte
7
Geralmente, as respostas apenas em código podem ser melhoradas com a adição de algumas explicações sobre como e por que funcionam. Ao adicionar uma resposta a uma pergunta de dois anos com as respostas existentes, é importante apontar qual novo aspecto da pergunta sua resposta aborda.
Jason Aller