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 protected]&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.

Zack
la source
Quel est le problème avec l'utilisation FormData?
invité271314
1
Je veux le publier comme "[email protected]&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.

poussée
la source
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; }
}
regnauld
la source
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 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>

invité271314
la source
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

Kamil Kiełczewski
la source
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
la source
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