Abrufen: POST-JSON-Daten

562

Ich versuche, ein JSON-Objekt mit fetch zu POSTEN .

Soweit ich verstehen kann, muss ich dem Text der Anfrage ein stringifiziertes Objekt hinzufügen, z.

fetch("/echo/json/",
{
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    method: "POST",
    body: JSON.stringify({a: 1, b: 2})
})
.then(function(res){ console.log(res) })
.catch(function(res){ console.log(res) })

Wenn Sie das json-Echo von jsfiddle verwenden, würde ich erwarten, dass das von mir gesendete Objekt ( {a: 1, b: 2}) zurückgesendet wird, aber dies geschieht nicht - chrome devtools zeigt den JSON nicht einmal als Teil der Anforderung an, was bedeutet, dass es nicht gesendet wird.

Rasierer
quelle
Welchen Browser verwenden Sie?
Krzysztof Safjanowski
@KrzysztofSafjanowski Chrom 42, die volle
Rasiermesser
Überprüfen Sie diese Geige jsfiddle.net/abbpbah4/2 Welche Daten erwarten Sie? weil die Anfrage von fiddle.jshell.net/echo/json ein leeres Objekt anzeigt . {}
Kaushik
@KaushikKishore bearbeitet, um die erwartete Ausgabe zu verdeutlichen. res.json()sollte zurückkehren {a: 1, b: 2}.
Rasiermesser
1
Sie haben vergessen, die jsonEigenschaft einzuschließen, die die Daten enthält, die Sie senden möchten. Allerdings wird das bodysowieso nicht richtig behandelt. Sehen Sie sich diese Geige an, um zu sehen, dass die Verzögerung von 5 Sekunden übersprungen wird. jsfiddle.net/99arsnkg Wenn Sie versuchen, zusätzliche Header hinzuzufügen, werden diese ignoriert. Dies ist wahrscheinlich ein Problem mit sich fetch()selbst.
Boombox

Antworten:

598

Mit ES2017- async/awaitUnterstützung können Sie POSTeine JSON-Nutzlast folgendermaßen erstellen :

(async () => {
  const rawResponse = await fetch('https://httpbin.org/post', {
    method: 'POST',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({a: 1, b: 'Textual content'})
  });
  const content = await rawResponse.json();

  console.log(content);
})();

Kann ES2017 nicht verwenden? Siehe @ vp_arts Antwort mit Versprechungen

Die Frage ist jedoch nach einem Problem, das durch einen längst behobenen Chrome-Fehler verursacht wurde.
Die ursprüngliche Antwort folgt.

chrome devtools zeigt den JSON nicht einmal als Teil der Anfrage an

Dies ist das eigentliche Problem hier und es ist ein Fehler mit Chrome Devtools , der in Chrome 46 behoben wurde.

Dieser Code funktioniert einwandfrei - er postet den JSON korrekt, er kann einfach nicht gesehen werden.

Ich würde erwarten, das Objekt zu sehen, das ich zurückgeschickt habe

Das funktioniert nicht, weil dies nicht das richtige Format für das Echo von JSfiddle ist .

Der richtige Code lautet:

var payload = {
    a: 1,
    b: 2
};

var data = new FormData();
data.append( "json", JSON.stringify( payload ) );

fetch("/echo/json/",
{
    method: "POST",
    body: data
})
.then(function(res){ return res.json(); })
.then(function(data){ alert( JSON.stringify( data ) ) })

Für Endpunkte, die JSON-Nutzdaten akzeptieren, ist der ursprüngliche Code korrekt

Rasierer
quelle
15
Für den Datensatz wird hier keine JSON-Nutzlast veröffentlicht - dies ist ein Formular post ( x-www-form-urlencoded) mit JSON-Daten in einem Feld mit dem Namen json. Die Daten werden also doppelt codiert. Für einen sauberen JSON-Beitrag siehe Antwort von @vp_arth unten.
mindplay.dk
1
@ mindplay.dk Dies ist kein x-www-form-urlencodierter Beitrag. Die Abruf-API verwendet immer eine mehrteilige / Formulardatencodierung für FormData-Objekte.
JukkaP
@JukkaP Ich stehe korrigiert. Mein Hauptpunkt war das Problem der doppelten Codierung.
mindplay.dk
2
Der Inhaltstyp ist immer noch Text / HTML. charset = iso-8859-1 weiß nicht, was ich falsch mache ...
KT Works
3
Um auf der sicheren Seite zu sein, ist es gut zu bestätigen, res.okfalls der Antwortcode ein Fehler ist. Es wäre auch gut, .catch()am Ende eine Klausel zu haben . Mir ist klar, dass dies nur ein Beispielausschnitt ist, aber denken Sie an diese Dinge für den realen Gebrauch.
Ken Lyon
206

Ich denke , Ihr Problem ist , jsfiddlekann Prozess form-urlencodedAnfrage nur.

Der richtige Weg, um eine JSON-Anfrage zu stellen, ist jedoch, jsonals Text korrekt zu übergeben:

fetch('https://httpbin.org/post', {
  method: 'post',
  headers: {
    'Accept': 'application/json, text/plain, */*',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({a: 7, str: 'Some string: &=&'})
}).then(res=>res.json())
  .then(res => console.log(res));

vp_arth
quelle
6
Dies ist die richtige Lösung, Punkt - alle anderen scheinen in Bezug auf x-www-form-urlencodedvs durcheinander zu sein application/json, entweder weil sie nicht übereinstimmen oder JSON doppelt in eine URL-codierte Zeichenfolge einschließen.
mindplay.dk
Aber das funktioniert bei jsfiddle nicht. Ich bin mir also nicht sicher, warum Sie sagen würden: "Dies ist die richtige Lösung, Punkt". Tun nicht alle anderen das Wrapping, um die API von jsfiddles /echoRoute zu erfüllen ?
Adam-Beck
69

Von Suchmaschinen kam ich zu diesem Thema für Nicht-JSON-Posting-Daten mit Fetch, also dachte ich, ich würde dies hinzufügen.

Für Nicht-JSON müssen Sie keine Formulardaten verwenden. Sie können den Content-TypeHeader einfach auf setzen application/x-www-form-urlencodedund eine Zeichenfolge verwenden:

fetch('url here', {
    method: 'POST',
    headers: {'Content-Type':'application/x-www-form-urlencoded'}, // this line is important, if this content-type is not set it wont work
    body: 'foo=bar&blah=1'
});

Eine alternative Möglichkeit, diese bodyZeichenfolge zu erstellen , anstatt sie wie oben beschrieben einzugeben, besteht darin, Bibliotheken zu verwenden. Zum Beispiel die stringifyFunktion von query-stringoder qsPakete. Wenn Sie dies verwenden, würde es folgendermaßen aussehen:

import queryString from 'query-string'; // import the queryString class

fetch('url here', {
    method: 'POST',
    headers: {'Content-Type':'application/x-www-form-urlencoded'}, // this line is important, if this content-type is not set it wont work
    body: queryString.stringify({for:'bar', blah:1}) //use the stringify object of the queryString class
});
Noitidart
quelle
2
Vielen Dank für die Abfragezeichenfolge. Ich habe es so oft mit JSON.stringify versucht, aber Ajax hat keine Antwort zurückgegeben. Aber die Abfragezeichenfolge hat es geschafft. Ich fand auch, dass es daran lag, fetch create json für body params zu erstellen, anstatt einen String zu erstellen.
Dänisch
1
Danke mann! Dies ist die beste Antwort! Ich habe gestern einige Stunden lang an die Wand geschlagen, um einen Weg zu finden, 'body' mit Formulardaten von meiner Webanwendung an meinen Server zu senden ... Ein Vorschlag: $ npm install cors --save Dies wird benötigt, um "loszuwerden" Modus: 'no-cors' "in
Abrufanfrage
Danke @AlexanderCherednichenko! Und danke, dass du diese Cors-Notiz geteilt hast, die interessant ist und von der ich nichts wusste. :)
Noitidart
1
Vielen Dank aus tiefstem Herzen. Du hast meine Zeit und auch mein Leben zweimal gerettet :)
Bafsar
1
Thnaks @bafsar!
Noitidart
42

Nachdem Sie einige Zeit damit verbracht haben, jsFiddle zurückzuentwickeln und zu versuchen, Nutzdaten zu generieren, gibt es einen Effekt.

Bitte achten Sie online return response.json();darauf, dass eine Antwort keine Antwort ist - sie ist vielversprechend.

var json = {
    json: JSON.stringify({
        a: 1,
        b: 2
    }),
    delay: 3
};

fetch('/echo/json/', {
    method: 'post',
    headers: {
        'Accept': 'application/json, text/plain, */*',
        'Content-Type': 'application/json'
    },
    body: 'json=' + encodeURIComponent(JSON.stringify(json.json)) + '&delay=' + json.delay
})
.then(function (response) {
    return response.json();
})
.then(function (result) {
    alert(result);
})
.catch (function (error) {
    console.log('Request failed', error);
});

jsFiddle: http://jsfiddle.net/egxt6cpz/46/ && Firefox> 39 && Chrome> 42

Krzysztof Safjanowski
quelle
Warum 'x-www-form-urlencodedstattdessen application/json? Was ist der Unterschied?
Juan Picado
@JuanPicado - nach jsfiddle Reverse Engineering vor 2 Jahren war es nur eine Option, dass es funktionieren könnte. Natürlich application/jsonist die richtige Form und es funktioniert jetzt. Danke für das gute Auge
:)
yw. Merkwürdiges Detail, es funktioniert bei mir auf die alte Art mit fetch( stackoverflow.com/questions/41984893/… ) statt dem application/json. Vielleicht wissen Sie warum ...
Juan Picado
6
Das Content-Typeist application/json, aber Ihre tatsächliche bodyscheint zu sein x-www-form-urlencoded- ich denke nicht, dass dies funktionieren sollte? Wenn es funktioniert, muss Ihr Server ziemlich verzeihend sein. Die Antwort von @vp_arth unten scheint die richtige zu sein.
mindplay.dk
18

Ich habe einen dünnen Wrapper um fetch () mit vielen Verbesserungen erstellt, wenn Sie eine reine JSON-REST-API verwenden:

// Small library to improve on fetch() usage
const api = function(method, url, data, headers = {}){
  return fetch(url, {
    method: method.toUpperCase(),
    body: JSON.stringify(data),  // send it as stringified json
    credentials: api.credentials,  // to keep the session on the request
    headers: Object.assign({}, api.headers, headers)  // extend the headers
  }).then(res => res.ok ? res.json() : Promise.reject(res));
};

// Defaults that can be globally overwritten
api.credentials = 'include';
api.headers = {
  'csrf-token': window.csrf || '',    // only if globally set, otherwise ignored
  'Accept': 'application/json',       // receive json
  'Content-Type': 'application/json'  // send json
};

// Convenient methods
['get', 'post', 'put', 'delete'].forEach(method => {
  api[method] = api.bind(null, method);
});

Um es zu verwenden, haben Sie die Variable apiund 4 Methoden:

api.get('/todo').then(all => { /* ... */ });

Und innerhalb einer asyncFunktion:

const all = await api.get('/todo');
// ...

Beispiel mit jQuery:

$('.like').on('click', async e => {
  const id = 123;  // Get it however it is better suited

  await api.put(`/like/${id}`, { like: true });

  // Whatever:
  $(e.target).addClass('active dislike').removeClass('like');
});
Francisco Presencia
quelle
Ich denke, Sie meinten eine andere Reihe von Argumenten als Object.assign? sollte sein, Object.assign({}, api.headers, headers)weil Sie nicht immer benutzerdefinierte headersin Hash von Common hinzufügen möchten api.headers. Recht?
Mobigital
@Mobigital völlig richtig, ich wusste damals nichts über diese Nuance, aber jetzt ist es der einzige Weg, wie ich es mache
Francisco Presencia
11

Dies hängt zusammen mit Content-Type. Wie Sie vielleicht aus anderen Diskussionen und Antworten auf diese Frage bemerkt haben, konnten einige Leute sie durch Einstellen lösen Content-Type: 'application/json'. Leider hat es in meinem Fall nicht funktioniert, meine POST-Anfrage war auf der Serverseite noch leer.

Wenn Sie es jedoch mit jQuery's versuchen $.post()und es funktioniert, liegt der Grund wahrscheinlich darin, dass jQuery Content-Type: 'x-www-form-urlencoded'anstelle von verwendet application/json.

data = Object.keys(data).map(key => encodeURIComponent(key) + '=' + encodeURIComponent(data[key])).join('&')
fetch('/api/', {
    method: 'post', 
    credentials: "include", 
    body: data, 
    headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
Marcus Lind
quelle
1
Mein Backend-Entwickler hat die API mit PHP erstellt und erwartet, dass die Daten wie Abfragezeichenfolgen und nicht wie ein JSON-Objekt sind. Dies löste die leere Antwort auf der Serverseite.
Eballeste
11

Hatte das gleiche Problem - nein bodywurde von einem Client an einen Server gesendet.

Das Hinzufügen eines Content-TypeHeaders hat es für mich gelöst:

var headers = new Headers();

headers.append('Accept', 'application/json'); // This one is enough for GET requests
headers.append('Content-Type', 'application/json'); // This one sends body

return fetch('/some/endpoint', {
    method: 'POST',
    mode: 'same-origin',
    credentials: 'include',
    redirect: 'follow',
    headers: headers,
    body: JSON.stringify({
        name: 'John',
        surname: 'Doe'
    }),
}).then(resp => {
    ...
}).catch(err => {
   ...
})
Grün
quelle
7

Die Top-Antwort funktioniert nicht für PHP7, weil es eine falsche Codierung hat, aber ich könnte die richtige Codierung mit den anderen Antworten herausfinden. Dieser Code sendet auch Authentifizierungscookies, die Sie wahrscheinlich benötigen, wenn Sie sich beispielsweise mit PHP-Foren befassen:

julia = function(juliacode) {
    fetch('julia.php', {
        method: "POST",
        credentials: "include", // send cookies
        headers: {
            'Accept': 'application/json, text/plain, */*',
            //'Content-Type': 'application/json'
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" // otherwise $_POST is empty
        },
        body: "juliacode=" + encodeURIComponent(juliacode)
    })
    .then(function(response) {
        return response.json(); // .text();
    })
    .then(function(myJson) {
        console.log(myJson);
    });
}
lama12345
quelle
3

Es könnte für jemanden nützlich sein:

Ich hatte das Problem, dass Formulardaten nicht für meine Anfrage gesendet wurden

In meinem Fall war es eine Kombination der folgenden Header, die ebenfalls das Problem verursachten, und der falsche Inhaltstyp.

Also habe ich diese beiden Header mit der Anfrage gesendet und die Formulardaten wurden nicht gesendet, als ich die funktionierenden Header entfernt habe.

"X-Prototype-Version" : "1.6.1",
"X-Requested-With" : "XMLHttpRequest"

Auch wenn andere Antworten darauf hindeuten, dass der Content-Type-Header korrekt sein muss.

Für meine Anfrage war der richtige Content-Type-Header:

"Inhaltstyp": "application / x-www-form-urlencoded; Zeichensatz = UTF-8"

Wenn Ihre Formulardaten also nicht an die Anforderung angehängt werden, können dies möglicherweise Ihre Header sein. Versuchen Sie, Ihre Header auf ein Minimum zu reduzieren, und fügen Sie sie dann nacheinander hinzu, um festzustellen, ob Ihr Problem behoben ist.

user_CC
quelle
3

Ich denke, wir müssen das JSON-Objekt nicht in eine Zeichenfolge analysieren. Wenn der Remote-Server json in die Anforderung akzeptiert, führen Sie einfach Folgendes aus:

const request = await fetch ('/echo/json', {
  headers: {
    'Content-type': 'application/json'
  },
  method: 'POST',
  body: { a: 1, b: 2 }
});

Wie die Curl-Anfrage

curl -v -X POST -H 'Content-Type: application/json' -d '@data.json' '/echo/json'

Falls der Remote-Dienst keine JSON-Datei als Text akzeptiert, senden Sie einfach eine Datenform:

const data =  new FormData ();
data.append ('a', 1);
data.append ('b', 2);

const request = await fetch ('/echo/form', {
  headers: {
    'Content-type': 'application/x-www-form-urlencoded'
  },
  method: 'POST',
  body: data
});

Wie die Curl-Anfrage

curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '@data.txt' '/echo/form'
Daniel García
quelle
2
Dies ist offensichtlich falsch. Es hat nichts mit der Serverseite zu tun, ob Sie Ihren JSON stringifizieren müssen oder nicht. Genau das curltut Ihr Befehl implizit! Wenn Sie Ihre Objekte nicht kennzeichnen, bevor Sie sie als übergeben, bodysenden Sie sie einfach "[object Object]"als Hauptteil Ihrer Anfrage. Ein einfacher Test in Dev Tools würde Ihnen das zeigen. Öffnen Sie es und versuchen Sie es, ohne diesen Tab zu verlassen:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
Oligofren
2

Wenn Ihre JSON-Nutzdaten Arrays und verschachtelte Objekte enthalten, würde ich URLSearchParams die param()Methode von jQuery verwenden.

fetch('/somewhere', {
  method: 'POST',
  body: new URLSearchParams($.param(payload))
})

Für Ihren Server sieht dies wie ein Standard-HTML aus <form>, das POSTbearbeitet wird.

Eric Sellin
quelle