Comment publier des données de formulaire avec Fetch Api?


118

Mon code:

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

J'ai essayé de publier mon formulaire en utilisant fetch api, et le corps qu'il envoie est comme:

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

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

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

(Je ne sais pas pourquoi le nombre dans la limite est changé à chaque fois qu'il envoie ...)

Je voudrais qu'il envoie les données avec "Content-Type": "application / x-www-form-urlencoded", que dois-je faire? Ou si je dois simplement m'en occuper, comment décoder les données de mon contrôleur?


À qui répondre à ma question, je sais que je peux le faire avec:

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

Ce que je veux, c'est quelque chose comme $ ("# form"). Serialize () dans jQuery (sans utiliser jQuery) ou la façon de décoder mulitpart / form-data dans le contrôleur. Merci pour vos réponses.


Quel est le problème avec l'utilisation FormData?
— invité271314

1
Je veux le publier comme "email=test@example.com&password=pw". C'est possible?
— Zack

1
«Je ne sais pas pourquoi le nombre dans la limite est changé à chaque fois qu'il envoie…» - L'identifiant de la frontière est juste un identifiant aléatoire, il peut être n'importe quoi et n'a aucune signification en soi. Il n'y a donc rien de mal à y choisir un nombre aléatoire (ce que font généralement les clients).
— poke

Réponses:


149

Pour citer MDN surFormData (c'est moi qui souligne):

L' FormDatainterface fournit un moyen de construire facilement un ensemble de paires clé / valeur représentant les champs de formulaire et leurs valeurs, qui peuvent ensuite être facilement envoyés à l'aide de la XMLHttpRequest.send()méthode. Il utilise le même format qu'un formulaire utiliserait si le type d'encodage était défini sur"multipart/form-data" .

Ainsi, lors de l'utilisation, FormDatavous vous enfermez dans multipart/form-data. Il n'y a aucun moyen d'envoyer un FormDataobjet en tant que corps et de ne pas envoyer de données au multipart/form-dataformat.

Si vous souhaitez envoyer les données en tant application/x-www-form-urlencodedque telles, vous devrez soit spécifier le corps sous forme de chaîne encodée en URL, soit transmettre un URLSearchParamsobjet. Ce dernier ne peut malheureusement pas être directement initialisé à partir d'un formélément. Si vous ne souhaitez pas parcourir vous-même les éléments de votre formulaire (ce que vous pouvez faire en utilisant HTMLFormElement.elements), vous pouvez également créer un URLSearchParamsobjet à partir d'un FormDataobjet:

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

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

Notez que vous n'avez pas besoin de spécifier Content-Typevous-même un en-tête.


Comme indiqué par monk-time dans les commentaires, vous pouvez également créer URLSearchParamset transmettre l' FormDataobjet directement, au lieu d'ajouter les valeurs dans une boucle:

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

Cependant, cela a encore un support expérimental dans les navigateurs, alors assurez-vous de le tester correctement avant de l'utiliser.


18
Vous pouvez également utiliser un objet ou simplement FormDatadans le constructeur directement au lieu d'une boucle:new URLSearchParams(new FormData(formElement))
— monk-time

@ monk-time Au moment de la rédaction de cette réponse, l'argument du constructeur URLSearchParamsétait très nouveau et avait un support très limité.
— poke le

3
désolé, ce n'était pas une plainte, juste une note à tous ceux qui liront ceci à l'avenir.
— monk-time le

1
@Prasanth Vous pouvez spécifier le type de contenu vous-même explicitement, mais vous devez choisir le bon . Il est plus facile de le laisser de côté et de s'en fetchoccuper pour vous.
— poke le

1
@chovy URLSearchParamsest intégré à la plupart des navigateurs modernes en tant qu'objet global et fonctionne également à partir de Node.
— piquer

67

Client

Ne définissez pas l'en-tête de type de contenu.

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

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

Serveur

Utilisez l' FromFormattribut pour spécifier que la source de liaison est des données de formulaire.

[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
Bien que cela fonctionne, cela n'envoie pas les données comme application/x-www-form-urlencodedce que OP demande.
— piquer

5
Pour moi, cela a fonctionné lorsque j'ai SUPPRIMÉ Content-Type de l'en-tête et laissé le navigateur le faire automatiquement. Merci!
— Chris

Merci @regnauld d'avoir essayé de travailler sur celui-ci toute la journée!
— ak85

1
Si vous ne définissez pas 'Content-type' pour Fetch, il le définira comme multipart/form-data, ce qui devrait être pour les données de formulaire! Ensuite, vous pouvez utiliser multerdans expressjs pour analyser facilement ce format de données.
— kyw le

23

Vous pouvez définir bodyune instance de URLSearchParamsavec une chaîne de requête passée en argument

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)est une façon particulièrement illisible de dire params.set(props[0], props[1]).
— poke

@poke Reflect.apply(params.set, params, props)est lisible de la perspective ici.
— invité271314

Cela semble être la seule réponse efficace ici: / merci! :)
— OZZIE

0

Utilisez FormDataet fetchpour saisir et envoyer des données


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
Les réponses au code uniquement peuvent généralement être améliorées en ajoutant des explications sur leur fonctionnement et leur raison d'être. Lorsque vous ajoutez une réponse à une question vieille de deux ans avec des réponses existantes, il est important de souligner le nouvel aspect de la question auquel votre réponse répond.
— Jason Aller
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.