Récupérer: POST json data

562

J'essaye de POSTER un objet JSON à l'aide de la récupération .

D'après ce que je peux comprendre, je dois attacher un objet stratifié au corps de la demande, par exemple:

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

Lorsque j'utilise l'écho json de jsfiddle, je m'attendrais à voir l'objet que j'ai envoyé ( {a: 1, b: 2}), mais cela ne se produit pas - chrome devtools n'affiche même pas le JSON dans le cadre de la demande, ce qui signifie qu'il n'est pas envoyé.

Rasoir
la source
Quel navigateur utilisez-vous?
Krzysztof Safjanowski
@KrzysztofSafjanowski chrome 42, qui est censé avoir une prise en charge complète
Razor
vérifiez ce violon jsfiddle.net/abbpbah4/2 à quelles données vous attendez? car obtenir la demande de fiddle.jshell.net/echo/json montre un objet vide. {}
Kaushik
@KaushikKishore a été modifié pour clarifier la sortie attendue. res.json()devrait revenir {a: 1, b: 2}.
Razor
1
Vous avez oublié d'inclure la jsonpropriété qui contient les données que vous souhaitez envoyer. Cependant, je bodyne suis pas traité correctement de toute façon. Voir ce violon pour voir que le délai de 5 secondes est ignoré. jsfiddle.net/99arsnkg De plus, lorsque vous essayez d'ajouter des en-têtes supplémentaires, ils sont ignorés. C'est probablement un problème avec fetch()lui-même.
boombox

Réponses:

600

Avec le async/awaitsupport ES2017 , voici comment utiliser POSTune charge utile 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);
})();

Vous ne pouvez pas utiliser ES2017? Voir la réponse de @ vp_art en utilisant des promesses

La question demande cependant un problème causé par un bug de chrome corrigé depuis longtemps.
La réponse originale suit.

chrome devtools n'affiche même pas le JSON dans le cadre de la demande

C'est le vrai problème ici , et c'est un bogue avec chrome devtools , corrigé dans Chrome 46.

Ce code fonctionne bien - il POSTE correctement le JSON, il ne peut tout simplement pas être vu.

Je m'attends à voir l'objet que j'ai renvoyé

cela ne fonctionne pas car ce n'est pas le format correct pour l'écho de JSfiddle .

Le bon code est:

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

Pour les points finaux acceptant les charges utiles JSON, le code d'origine est correct

Rasoir
la source
15
Pour mémoire, il ne s'agit pas de publier une charge utile JSON - il s'agit d'un formulaire post ( x-www-form-urlencoded) avec des données JSON dans un champ nommé json. Les données sont donc doublement codées. Pour un article JSON propre, voir la réponse de @vp_arth ci-dessous.
mindplay.dk
1
@ mindplay.dk Ce n'est pas un message x-www-form-urlencoded. L'API Fetch utilise toujours le codage multipart / form-data sur les objets FormData.
JukkaP
@JukkaP Je me tiens corrigé. Mon point principal était le problème du double encodage.
mindplay.dk
2
Le type de contenu est toujours text / html; charset = iso-8859-1 ne sais pas ce que je fais mal ...
KT Works
3
Pour être prudent, il serait bon de confirmer res.okau cas où le code de réponse serait une sorte d'erreur. Il serait également bon d'avoir une .catch()clause à la fin. Je me rends compte que ce n'est qu'un exemple d'extrait, mais gardez ces choses à l'esprit pour une utilisation dans le monde réel.
Ken Lyon
207

Je pense que votre problème est de jsfiddlepouvoir traiter la form-urlencodeddemande uniquement.

Mais la bonne façon de faire une demande json est de passer correctement en jsontant que corps:

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
la source
6
C'est la bonne solution, point final - tout le monde semble être mélangé à propos de x-www-form-urlencodedvs application/json, soit en les dépareillant, soit en encapsulant JSON dans des chaînes encodées en URL.
mindplay.dk
Mais cela ne fonctionne pas pour jsfiddle. Donc, je ne suis pas sûr de comprendre pourquoi vous diriez "C'est la bonne solution, point final". Tout le monde ne fait-il pas le wrapper pour satisfaire l'API de l' /echoitinéraire de jsfiddle ?
adam-beck
69

Depuis les moteurs de recherche, je me suis retrouvé sur ce sujet pour les données de publication non json avec fetch, alors j'ai pensé l'ajouter.

Pour les non-json, vous n'avez pas besoin d'utiliser les données du formulaire. Vous pouvez simplement définir l'en- Content-Typetête application/x-www-form-urlencodedet utiliser une chaîne:

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

Une autre façon de construire cette bodychaîne, plutôt que de la taper comme je l'ai fait ci-dessus, est d'utiliser des bibliothèques. Par exemple la stringifyfonction from query-stringou qspackages. Donc, en utilisant cela, cela ressemblerait à:

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
la source
2
merci beaucoup pour la chaîne de requête, j'ai essayé tant de fois avec JSON.stringify mais ajax ne retournait pas de réponse. mais la chaîne de requête a fait l'affaire. J'ai également constaté que c'était parce que fetch create json pour les paramètres du corps au lieu de créer une chaîne.
Danois
1
Merci mec! Ceci est la meilleure réponse! J'ai heurté le mur hier pendant quelques heures en essayant de trouver un moyen d'envoyer "corps" avec les données du formulaire de mon application Web à mon serveur ... Une suggestion: $ npm install cors --save Ceci est nécessaire pour se débarrasser de " mode: 'no-cors' "dans la demande de récupération, voir github.com/expressjs/cors
Alexander Cherednichenko
Merci @AlexanderCherednichenko! Et merci d'avoir partagé cette note de cors, c'est une question intéressante que je ne connaissais pas. :)
Noitidart
1
Merci du plus profond de mon cœur. Vous avez sauvé mon temps et ma vie à deux reprises :)
bafsar
1
Thnaks @bafsar!
Noitidart
42

Après avoir passé quelques temps, inverser l'ingénierie jsFiddle, essayer de générer de la charge utile - il y a un effet.

Veuillez prendre soin (attention) en ligne return response.json();lorsque la réponse n'est pas une réponse - c'est une promesse.

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
la source
Pourquoi à la 'x-www-form-urlencodedplace application/json? Quelle est la différence?
Juan Picado
@JuanPicado - après l'ingénierie inverse de jsfiddle il y a 2 ans, ce n'était qu'une option qui pouvait fonctionner. Bien sûr, application/jsonc'est la bonne forme et cela fonctionne maintenant. Merci pour le bon œil
:)
yw. Détail curieux, cela fonctionne pour moi à l'ancienne avec fetch( stackoverflow.com/questions/41984893/… ) au lieu de application/json. Peut-être savez-vous pourquoi ...
Juan Picado
6
C'est Content-Typevrai application/json, mais votre réel bodysemble être x-www-form-urlencoded- je ne pense pas que cela devrait fonctionner? Si cela fonctionne, votre serveur doit être assez indulgent. La réponse de @vp_arth ci-dessous semble être la bonne.
mindplay.dk
18

J'ai créé un wrapper fin autour de fetch () avec de nombreuses améliorations si vous utilisez une API REST purement 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);
});

Pour l'utiliser, vous avez la variable apiet 4 méthodes:

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

Et au sein d'une asyncfonction:

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

Exemple avec 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
la source
Je pense que vous vouliez dire un ensemble d'arguments différent Object.assign? devrait être Object.assign({}, api.headers, headers)parce que vous ne voulez pas continuer à ajouter des personnalisations headersdans le hachage commun api.headers. droite?
Mobigital
@Mobigital tout à fait raison, je ne connaissais pas cette nuance à l'époque, mais maintenant c'est la seule façon de le faire
Francisco Presencia
11

Ceci est lié à Content-Type. Comme vous l'avez peut-être remarqué lors d'autres discussions et réponses à cette question, certaines personnes ont pu la résoudre en définissant Content-Type: 'application/json'. Malheureusement dans mon cas cela n'a pas fonctionné, ma requête POST était toujours vide côté serveur.

Cependant, si vous essayez avec jQuery $.post()et que cela fonctionne, la raison est probablement que jQuery utilise Content-Type: 'x-www-form-urlencoded'au lieu de 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
la source
1
Mon développeur backend a construit l'API avec PHP, s'attendait à ce que les données soient des chaînes de requête, pas un objet json. Cela a résolu la réponse vide du côté serveur.
eballeste
11

Eu le même problème - non body été envoyé d'un client à un serveur.

L'ajout d'un en- Content-Typetête l'a résolu pour moi:

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

La première réponse ne fonctionne pas pour PHP7, car son encodage est incorrect, mais je pourrais trouver le bon encodage avec les autres réponses. Ce code envoie également des cookies d'authentification, que vous souhaiterez probablement lorsque vous traitez, par exemple, avec des forums 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);
    });
}
lama12345
la source
3

Cela pourrait être utile à quelqu'un:

J'avais le problème que les données du formulaire n'étaient pas envoyées pour ma demande

Dans mon cas, c'était une combinaison des en-têtes suivants qui causaient également le problème et le mauvais type de contenu.

J'envoyais donc ces deux en-têtes avec la demande et ce n'était pas l'envoi des données de formulaire lorsque j'ai supprimé les en-têtes qui fonctionnaient.

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

De même, comme d'autres réponses suggèrent que l'en-tête Content-Type doit être correct.

Pour ma demande, l'en-tête de type de contenu correct était:

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

Donc, en fin de compte, si vos données de formulaire ne sont pas jointes à la demande, cela pourrait potentiellement être vos en-têtes. Essayez de réduire au minimum vos en-têtes, puis essayez de les ajouter un par un pour voir si votre problème est résolu.

user_CC
la source
3

Je pense que, nous n'avons pas besoin d'analyser l'objet JSON dans une chaîne, si le serveur distant accepte json dans leur demande, exécutez simplement:

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

Tels que la demande de boucle

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

Dans le cas où le serveur distant n'accepte pas un fichier json comme corps, envoyez simplement un dataForm:

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

Tels que la demande de boucle

curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '@data.txt' '/echo/form'
Daniel García
la source
2
C'est manifestement incorrect. Cela n'a rien à voir avec le côté serveur, que vous ayez ou non besoin de stringifier votre json. C'est exactement ce que curlfait implicitement votre commande! Si vous ne strifiez pas vos objets avant de les passer comme bodyvous les enverrez simplement "[object Object]"comme corps de votre demande. Un simple test dans Dev Tools vous le montrerait. Ouvrez-le et essayez ceci sans quitter cet onglet:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
oligofren
2

Si votre charge utile JSON contient des tableaux et des objets imbriqués, j'utiliserais URLSearchParams la param()méthode de jQuery .

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

Pour votre serveur, cela ressemblera à un HTML standard en <form>cours d' POSTédition.

Eric Sellin
la source