Wie poste ich Formulardaten mit Fetch API?

115

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

Ich habe versucht, mein Formular mit der Abruf-API zu veröffentlichen, und der gesendete Text lautet wie folgt:

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

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

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

(Ich weiß nicht, warum die Nummer in der Grenze jedes Mal geändert wird, wenn sie sendet ...)

Ich möchte, dass die Daten mit "Content-Type" gesendet werden: "application / x-www-form-urlencoded". Was soll ich tun? Oder wie dekodiere ich die Daten in meinem Controller, wenn ich mich nur darum kümmern muss?


Wem meine Frage beantwortet, ich weiß, dass ich es tun kann mit:

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

Was ich möchte, ist so etwas wie $ ("# form"). Serialize () in jQuery (ohne Verwendung von jQuery) oder die Art und Weise, Mulitpart- / Formulardaten im Controller zu dekodieren. Vielen Dank für Ihre Antworten.

Zack
quelle
Was ist ein Problem bei der Verwendung FormData?
guest271314
1
Ich möchte es als "[email protected]&password=pw" veröffentlichen. Ist es möglich?
Zack
1
"Ich weiß nicht, warum die Zahl in der Grenze jedes Mal geändert wird, wenn sie sendet ..." - Die Grenzkennung ist nur eine zufällige Kennung, sie kann alles sein und hat für sich genommen keine Bedeutung. Es ist also nichts Falsches daran, dort eine Zufallszahl zu wählen (was Kunden normalerweise tun).
stupsen

Antworten:

147

Um MDNFormData zu zitieren (Hervorhebung von mir):

Die FormDataSchnittstelle bietet eine Möglichkeit, auf einfache Weise eine Reihe von Schlüssel / Wert-Paaren zu erstellen, die Formularfelder und deren Werte darstellen, die dann mithilfe der XMLHttpRequest.send()Methode einfach gesendet werden können . Es verwendet dasselbe Format, das ein Formular verwenden würde, wenn der Codierungstyp festgelegt wäre"multipart/form-data" .

Wenn FormDataSie also verwenden, schließen Sie sich ein multipart/form-data. Es gibt keine Möglichkeit, ein FormDataObjekt als Hauptteil zu senden und keine Daten im multipart/form-dataFormat zu senden .

Wenn Sie die Daten so senden möchten, müssen application/x-www-form-urlencodedSie entweder den Text als URL-codierte Zeichenfolge angeben oder ein URLSearchParamsObjekt übergeben. Letzteres kann leider nicht direkt aus einem formElement initialisiert werden . Wenn Sie Ihre Formularelemente nicht selbst durchlaufen möchten (was Sie auch tun könntenHTMLFormElement.elements ), können Sie auch ein URLSearchParamsObjekt aus einem FormDataObjekt erstellen :

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

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

Beachten Sie, dass Sie selbst keinen Content-TypeHeader angeben müssen .


Wie von monk-time in den Kommentaren angegeben, können Sie URLSearchParamsdas FormDataObjekt auch direkt erstellen und übergeben , anstatt die Werte in einer Schleife anzuhängen:

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

Dies hat jedoch noch einige experimentelle Unterstützung in Browsern. Testen Sie dies daher ordnungsgemäß, bevor Sie es verwenden.

Sack
quelle
18
Sie können auch ein Objekt oder nur FormDataim Konstruktor direkt anstelle einer Schleife verwenden:new URLSearchParams(new FormData(formElement))
Mönch-Zeit
@ Mönch-Zeit das Konstruktor Argument , dass die Antwort zu schreiben, zu der Zeit URLSearchParamswar sehr neu und war sehr begrenzt Unterstützung.
stupsen
3
Entschuldigung, das war keine Beschwerde, nur eine Notiz an alle, die dies in Zukunft lesen werden.
Mönch-Zeit
1
@Prasanth Sie können den Inhaltstyp explizit selbst angeben, müssen jedoch den richtigen auswählen . Es ist einfacher, es einfach wegzulassen und fetches für Sie zu erledigen.
Poke
1
@chovy URLSearchParamsist in den meisten modernen Browsern als globales Objekt integriert und funktioniert auch über Node.
Poke
67

Klient

Legen Sie den Inhaltstyp-Header nicht fest.

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

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

Server

Verwenden Sie das FromFormAttribut, um anzugeben, dass es sich bei der Bindungsquelle um Formulardaten handelt.

[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
quelle
4
Während dies funktioniert, werden die Daten nicht so gesendet, wie application/x-www-form-urlencodedes OP verlangt.
stupsen
5
Bei mir hat es funktioniert, als ich mich Content-Type aus dem Header entfernt habe und den Browser es automatisch machen ließ. Vielen Dank!
Chris
Danke @regnauld habe den ganzen Tag versucht, dieses Problem zu lösen!
Ak85
1
Wenn Sie für "Abrufen" nicht "Inhaltstyp" festlegen, wird dies als "Festlegen" festgelegt, wie multipart/form-dataes für Formulardaten sein sollte! Dann können Sie multerin expressjs verwenden, um dieses Datenformat einfach zu analysieren.
Kyw
23

Sie können bodyeine Instanz festlegen , deren URLSearchParamsAbfragezeichenfolge als Argument übergeben wird

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
quelle
2
Reflect.apply(params.set, params, props)ist eine besonders unlesbare Art zu sagen params.set(props[0], props[1]).
stupsen
@poke Reflect.apply(params.set, params, props)ist hier aus der Perspektive lesbar.
guest271314
Dies scheint hier die einzige funktionierende Antwort zu sein: / Danke! :)
OZZIE
0

Verwenden Sie FormDataund fetch, um Daten zu erfassen und zu senden

Kamil Kiełczewski
quelle
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
quelle
7
Nur-Code-Antworten können im Allgemeinen verbessert werden, indem erklärt wird, wie und warum sie funktionieren. Wenn Sie eine Antwort auf eine zwei Jahre alte Frage mit vorhandenen Antworten hinzufügen, ist es wichtig, darauf hinzuweisen, welchen neuen Aspekt der Frage Ihre Antwort anspricht.
Jason Aller