¿Cómo publico datos de formularios con fetch api?


117

Mi 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",
}

Intenté publicar mi formulario usando fetch api, y el cuerpo que envía es como:

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

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

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

(No sé por qué se cambia el número en el límite cada vez que envía ...)

Me gustaría que envíe los datos con "Content-Type": "application / x-www-form-urlencoded", ¿qué debo hacer? O si solo tengo que lidiar con eso, ¿cómo decodifico los datos en mi controlador?


A quien responda mi pregunta, sé que puedo hacerlo con:

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

Lo que quiero es algo como $ ("# formulario"). Serialize () en jQuery (sin usar jQuery) o la forma de decodificar mulitpart / form-data en el controlador. Sin embargo, gracias por tus respuestas.


¿Cuál es el problema con el uso FormData?
— guest271314

1
Quiero publicarlo como "email=test@example.com&password=pw". ¿Es posible?
— Zack

1
“No sé por qué el número en el límite se cambia cada vez que envía…” - El identificador de límite es simplemente un identificador aleatorio, puede ser cualquier cosa y no tiene ningún significado por sí solo. Así que no hay nada de malo en elegir un número aleatorio allí (que es lo que suelen hacer los clientes).
— empuje el

Respuestas:


149

Para citar MDN enFormData (el énfasis es mío):

La FormDatainterfaz proporciona una forma de construir fácilmente un conjunto de pares clave / valor que representan campos de formulario y sus valores, que luego se pueden enviar fácilmente mediante el XMLHttpRequest.send()método. Utiliza el mismo formato que usaría un formulario si se configurara el tipo de codificación"multipart/form-data" .

Entonces, cuando lo usas, FormDatate estás encerrando en multipart/form-data. No hay forma de enviar un FormDataobjeto como cuerpo y no enviar datos en el multipart/form-dataformato.

Si desea enviar los datos, application/x-www-form-urlencodedtendrá que especificar el cuerpo como una cadena codificada en URL o pasar un URLSearchParamsobjeto. Desafortunadamente, este último no se puede inicializar directamente desde un formelemento. Si no desea iterar a través de los elementos de su formulario usted mismo (lo que podría hacer usando HTMLFormElement.elements), también puede crear un URLSearchParamsobjeto a partir de un 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(…);

Tenga en cuenta que no es necesario que especifique un Content-Typeencabezado usted mismo.


Como señaló monk-time en los comentarios, también puede crear URLSearchParamsy pasar el FormDataobjeto directamente, en lugar de agregar los valores en un bucle:

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

Sin embargo, esto todavía tiene cierto soporte experimental en los navegadores, así que asegúrese de probarlo correctamente antes de usarlo.


18
También puede usar un objeto o simplemente FormDataen el constructor directamente en lugar de un bucle:new URLSearchParams(new FormData(formElement))
— monk-time

@ monk-time En el momento de escribir esa respuesta, el argumento del constructor de URLSearchParamsera muy nuevo y tenía un soporte muy limitado.
— empuje el

3
lo siento, eso no fue una queja, solo una nota para todos los que leerán esto en el futuro.
— hora del monje el

1
@Prasanth Puede especificar el tipo de contenido usted mismo explícitamente, pero debe elegir el correcto . Es más fácil dejarlo y fetchencargarse de él por usted.
— empuje el

1
@chovy URLSearchParamsestá integrado en la mayoría de los navegadores modernos como un objeto global y también funciona desde Node.
— empuje el

67

Cliente

No establezca el encabezado de tipo de contenido.

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

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

Servidor

Utilice el FromFormatributo para especificar que la fuente de enlace son datos de formulario.

[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; }
}

4
Si bien esto funciona, esto no envía los datos, ya application/x-www-form-urlencodedque es lo que solicita OP.
— empuje el

5
Para mí, funcionó cuando quité Content-Type del encabezado y dejé que el navegador lo hiciera automáticamente. ¡Gracias!
— Chris

¡Gracias @regnauld han estado tratando de resolver esto todo el día!
— ak85

1
Si no configura 'Tipo de contenido' para Fetch, lo configurará como multipart/form-data, que es lo que debería ser para los datos del formulario. Luego, puede usar multeren expressjs para analizar ese formato de datos fácilmente.
— kyw

23

Puede establecer bodyuna instancia de URLSearchParamscon una cadena de consulta pasada como argumento

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>


2
Reflect.apply(params.set, params, props)es una forma de decir particularmente ilegible params.set(props[0], props[1]).
— toque el

@poke Reflect.apply(params.set, params, props)se puede leer desde la perspectiva aquí.
— guest271314

Esta parece ser la única respuesta que funciona aquí: / ¡gracias! :)
— OZZIE

0

Utilice FormDatay fetchpara capturar y enviar datos


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
Las respuestas de solo código generalmente se pueden mejorar agregando alguna explicación de cómo y por qué funcionan. Cuando agregue una respuesta a una pregunta de dos años con respuestas existentes, es importante señalar qué aspecto nuevo de la pregunta aborda su respuesta.
— Jason Aller
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.