Recupera: dati JSON POST


562

Sto provando a POSTARE un oggetto JSON usando fetch .

Da quello che posso capire, ho bisogno di allegare un oggetto rigoroso al corpo della richiesta, ad esempio:

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) })

Quando uso l'eco json di jsfiddle mi aspetto di vedere l'oggetto che ho inviato ( {a: 1, b: 2}) indietro, ma ciò non accade - Chrome Devtools non mostra nemmeno JSON come parte della richiesta, il che significa che non viene inviato.


Quale browser utilizzate?
Krzysztof Safjanowski il

@KrzysztofSafjanowski chrome 42, che dovrebbe avere il pieno supporto per il recupero
Rasoio

controlla questo violino jsfiddle.net/abbpbah4/2 quali dati ti aspetti? perché ottenere la richiesta di fiddle.jshell.net/echo/json mostra un oggetto vuoto. {}
Kaushik,

@KaushikKishore modificato per chiarire l'output previsto. res.json()dovrebbe tornare {a: 1, b: 2}.
Rasoio

1
Hai dimenticato di includere la jsonproprietà che contiene i dati che desideri inviare. Tuttavia, io bodynon sono trattato correttamente comunque. Vedi questo violino per vedere che il ritardo di 5 secondi viene saltato. jsfiddle.net/99arsnkg Inoltre, quando si tenta di aggiungere ulteriori intestazioni, vengono ignorate. Questo è probabilmente un problema con fetch()se stesso.
boombox,

Risposte:


599

Con il async/awaitsupporto ES2017 , ecco come effettuare POSTun payload JSON:

(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);
})();

Non puoi usare ES2017? Vedi la risposta di @ vp_art usando le promesse

La domanda, tuttavia, è quella di richiedere un problema causato da un bug Chrome fisso da tempo.
Segue la risposta originale.

chrome devtools non mostra nemmeno JSON come parte della richiesta

Questo è il vero problema qui , ed è un bug con Chrome Devtools , risolto in Chrome 46.

Quel codice funziona benissimo - sta postando correttamente JSON, semplicemente non può essere visto.

Mi aspetto di vedere l'oggetto che ho rispedito

non funziona perché non è il formato corretto per l'eco di JSfiddle .

Il codice corretto è:

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 ) ) })

Per gli endpoint che accettano payload JSON, il codice originale è corretto


15
Per la cronaca, questo non sta pubblicando un payload JSON - questo è un post modulo ( x-www-form-urlencoded) con dati JSON in un campo chiamato json. Quindi i dati sono doppiamente codificati. Per un post JSON pulito, vedere la risposta di @vp_arth di seguito.
mindplay.dk

1
@ mindplay.dk Questo non è un post codificato x-www-form-urlenced. L'API di recupero utilizza sempre la codifica multipart / form-data sugli oggetti FormData.
JukkaP,

@JukkaP Sono corretto. Il mio punto principale era il problema della doppia codifica.
mindplay.dk,

2
Content-Type è ancora text / html; charset = iso-8859-1 non so cosa sto facendo di sbagliato ...
KT funziona

3
Per essere al sicuro, sarebbe bene confermare res.oknel caso in cui il codice di risposta sia un qualche tipo di errore. Sarebbe anche bello avere una .catch()clausola alla fine. Mi rendo conto che questo è solo un frammento di esempio, ma tengo presente queste cose per l'uso nel mondo reale.
Ken Lyon,

206

Penso che il tuo problema sia che jsfiddlepuò elaborare form-urlencodedsolo la richiesta.

Ma il modo corretto di fare una richiesta json è passare correttamente jsoncome corpo:

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));


6
Questa è la soluzione corretta, periodo - tutti gli altri sembra essere mescolati su x-www-form-urlencodedvs application/json, o loro o doppio involucro JSON mismatching in stringhe con codifica URL.
mindplay.dk,

Ma questo non funziona per jsfiddle. Quindi, non sono sicuro di capire perché dovresti dire "Questa è la soluzione corretta, punto". Tutti gli altri non stanno eseguendo il wrapping per soddisfare l'API del /echopercorso di jsfiddle ?
adam-beck,

69

Dai motori di ricerca, sono finito su questo argomento per pubblicare dati non json con fetch, quindi ho pensato di aggiungere questo.

Per i non json non è necessario utilizzare i dati del modulo. Puoi semplicemente impostare l' Content-Typeintestazione su application/x-www-form-urlencodede usare una stringa:

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'
});

Un modo alternativo per costruire quella bodystringa, piuttosto che digitarla come ho fatto sopra, è usare le librerie. Ad esempio la stringifyfunzione da query-stringo qspacchetti. Quindi usando questo sembrerebbe:

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

2
grazie mille per la stringa di query, ho provato così tante volte con JSON.stringify ma ajax non stava rispondendo. ma la stringa di query ha funzionato. Ho anche scoperto che è stato perché recuperare crea json per i parametri del corpo invece di creare una stringa.
Danese

1
Grazie amico! Questa è la risposta migliore! Ieri ho colpito il muro per alcune ore cercando di trovare un modo per inviare 'body' con i dati del modulo dalla mia applicazione web al mio server ... Un suggerimento: $ npm install cors --save Questo è necessario per sbarazzarsi di " mode: "no-cors" "nella richiesta di Fetch vedi github.com/expressjs/cors
Alexander Cherednichenko,

Grazie @AlexanderCherednichenko! E grazie per aver condiviso quella nota cors che è una interessante che non conoscevo. :)
Noitidart,

1
Grazie dal profondo del mio cuore. Mi hai salvato il tempo e anche la vita due volte :)
bafsar

1
Thnaks @bafsar!
Noitidart,

42

Dopo aver trascorso alcune volte, esegui il reverse engineering di jsFiddle, provando a generare payload - c'è un effetto.

Si prega di prestare attenzione (attenzione) in linea return response.json();dove la risposta non è una risposta - è promessa.

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


Perché 'x-www-form-urlencodedinvece application/json? Qual è la differenza?
Juan Picado,

@JuanPicado - dopo il reverse engineering di jsfiddle 2 anni fa era solo un'opzione che poteva funzionare. Naturalmente application/jsonè la forma corretta e funziona ora. Grazie per l'occhio buono
:)

yw. Dettaglio curioso, funziona per me alla vecchia maniera con fetch( stackoverflow.com/questions/41984893/… ) invece che con application/json. Forse sai perché ...
Juan Picado,

6
Il Content-Typeè application/json, ma le vostre effettive bodysembra essere x-www-form-urlencoded- non credo che questo dovrebbe funzionare? Se funziona, il tuo server deve essere abbastanza indulgente. La risposta di @vp_arth di seguito sembra essere quella corretta.
mindplay.dk

18

Ho creato un wrapper sottile attorno a fetch () con molti miglioramenti se stai usando un'API REST puramente json:

// 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);
});

Per usarlo hai la variabile apie 4 metodi:

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

E all'interno di una asyncfunzione:

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

Esempio con 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');
});

Penso che volevi dire una serie diversa di argomenti per Object.assign? dovrebbe essere Object.assign({}, api.headers, headers)perché non si desidera continuare ad aggiungere personalizzato headersnell'hash del comune api.headers. destra?
Mobigital

@Mobigital ha perfettamente ragione, allora non conoscevo quella sfumatura ma ora è l'unico modo per farlo
Francisco Presencia

11

Questo è legato a Content-Type. Come avrete notato da altre discussioni e risposte a questa domanda, alcune persone sono state in grado di risolverlo impostando Content-Type: 'application/json'. Sfortunatamente nel mio caso non ha funzionato, la mia richiesta POST era ancora vuota sul lato server.

Tuttavia, se provi con jQuery $.post()e funziona, il motivo è probabilmente a causa dell'utilizzo di jQuery Content-Type: 'x-www-form-urlencoded'anziché 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'}
})

1
Il mio sviluppatore back-end ha creato l'API con PHP, si aspettava che i dati fossero simili a una stringa di query, non a un oggetto json. Ciò ha risolto la risposta vuota sul lato server.
eballeste,

11

Aveva lo stesso problema: non è bodystato inviato da un client a un server.

L'aggiunta Content-Typedell'intestazione ha risolto il problema per me:

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 => {
   ...
})

7

La risposta migliore non funziona per PHP7, perché ha una codifica errata, ma ho potuto capire la codifica corretta con le altre risposte. Questo codice invia anche cookie di autenticazione, che probabilmente desideri quando hai a che fare con, ad esempio, i forum PHP:

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

3

Potrebbe essere utile a qualcuno:

Avevo il problema che i formdata non venivano inviati per la mia richiesta

Nel mio caso è stata una combinazione delle seguenti intestazioni a causare il problema e il tipo di contenuto errato.

Quindi stavo inviando queste due intestazioni con la richiesta e non stava inviando i formdata quando ho rimosso le intestazioni che funzionavano.

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

Inoltre, poiché altre risposte suggeriscono che l'intestazione Content-Type deve essere corretta.

Per la mia richiesta l'intestazione Content-Type corretta era:

"Content-Type": "application / x-www-form-urlencoded; charset = UTF-8"

Quindi, in conclusione, se i tuoi formdata non vengono allegati alla richiesta, potrebbero essere le intestazioni. Prova a ridurre al minimo le intestazioni, quindi prova ad aggiungerle una ad una per vedere se il problema è stato risolto.


3

Penso che, non abbiamo bisogno di analizzare l'oggetto JSON in una stringa, se il server remoto accetta json nella richiesta, basta eseguire:

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

Come la richiesta di arricciatura

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

Nel caso in cui il servizio remoto non accetti un file json come corpo, basta inviare un form dati:

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

Come la richiesta di arricciatura

curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '@data.txt' '/echo/form'

2
Questo è palesemente errato. Non ha nulla a che fare con il lato server indipendentemente dal fatto che tu debba o meno stringere il tuo json. Questo è esattamente ciò che il tuo curlcomando sta facendo implicitamente! Se non stringi i tuoi oggetti prima di passarli come li bodyinvierai semplicemente "[object Object]"come corpo della tua richiesta. Un semplice test in Dev Tools te lo mostrerebbe. Aprilo e prova questo senza lasciare questa scheda:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
oligofren

2

Se il tuo payload JSON contiene array e oggetti nidificati, utilizzerei URLSearchParams il param()metodo di jQuery .

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

Al tuo server, sembrerà un HTML standard in <form>fase di modifica POST.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.