2015-04-21 1 views
209

J'essaye de POSTER un objet JSON en utilisant fetch.Fetch: données POST json

D'après ce que je peux comprendre, je dois joindre un objet au corps de chaîne de caractères 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 vous utilisez jsfiddle's json echo j'attendre à voir l'objet que j'ai envoyé ({a: 1, b: 2}) retour, mais cela n'arrive 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é.

+0

Quel navigateur utilisez-vous? –

+0

@KrzysztofSafjanowski chrome 42, qui est censé avoir [soutien complet] (http://caniuse.com/#search=fetch) – Razor

+0

vérifier ce violon https://jsfiddle.net/abbpbah4/2/ quelles données vous ' vous attendez? car obtenir la requête de https://fiddle.jshell.net/echo/json montre un objet vide. '{}' –

Répondre

137

Les devtools de chrome ne montre même pas le JSON dans le cadre de la demande

C'est la vrai problème ici, et c'est un bug with chrome devtools, corrigé dans Chrome 46.

Ce code fonctionne très bien - il est POSTing correctement le JSON, il ne peut tout simplement pas être vu.

J'espère voir l'objet que je l'ai renvoyé

qui ne fonctionne pas parce que ce n'est pas le correct format for JSfiddle's echo.

Le correct 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 noeuds finaux acceptant des charges utiles JSON, le code d'origine est correct

+7

Pour l'enregistrement, cela ne publie pas une charge JSON - il s'agit d'un formulaire ('x-www-form-urlencoded') avec une donnée JSON dans un champ nommé' json'. Les données sont donc doublement codées. Pour un post JSON propre, consultez la réponse de @vp_arth ci-dessous. –

24

Après avoir passé quelques instants, effectuez un reverse engineering de jsFiddle, en essayant de générer la charge utile - il y a un effet.

S'il vous plaît prenez un œil (soin) sur la ligne return response.json(); où la réponse n'est pas une réponse - c'est prometteur.

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

+0

Pourquoi "x-www-form-urlencoded' à la place de" application/json "? Quelle est la différence? –

+0

@JuanPicado - après ** jsfiddle ** reverse engineering il y a 2 ans ce n'était qu'une option qui pouvait fonctionner. Bien sûr 'application/json' est la forme correcte et cela fonctionne maintenant. Merci pour le bon oeil:) –

+0

yw. Détail curieux, ça marche pour moi à l'ancienne avec 'fetch' (http://stackoverflow.com/questions/41984893/fetch-post-fails-on-post-json-body-with-sails-js?noredirect = 1 # comment71171442_41984893) à la place de l'application/json. Peut-être savez-vous pourquoi ... –

24

des moteurs de recherche, je me suis retrouvé sur ce sujet pour l'affichage de données non-JSON avec fetch , alors je pensais que j'ajouterais ceci.

Pour non-json vous n'avez pas besoin d'utiliser les données de formulaire. Vous pouvez simplement l'en-tête Content-Type à application/x-www-form-urlencoded et 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 chaîne body, plutôt taper comme je l'ai fait ci-dessus, est d'utiliser les bibliothèques. Par exemple la fonction stringify de query-string ou qs paquets. Donc, en utilisant cela, il ressemblerait à ceci:

import queryString from 'query-string'; 
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} 
}); 
+1

merci beaucoup pour la chaîne de requête, j'ai essayé tant de fois avec JSON.stringify mais ajax ne retournait pas la réponse. mais la chaîne de requête a fait l'affaire. J'ai aussi trouvé que c'était parce que fetch crée des json pour les paramètres de corps au lieu de créer une chaîne. – Danish

+1

Merci l'homme! C'est la meilleure réponse! Je frappais le mur hier pour quelques heures en essayant de trouver un moyen d'envoyer 'corps' avec les données de formulaire de mon application web à mon serveur ... Une suggestion: $ npm installer cors --save Ceci est nécessaire pour se débarrasser de " mode: 'no-cors' "dans Fetch request voir https://github.com/expressjs/cors –

+0

Merci @AlexanderCherednichenko! Et merci de partager cette note cors c'est un intéressant que je ne connaissais pas. :) – Noitidart

94

Je pense que votre question est jsfiddle peut traiter form-urlencoded demande.

Mais bonne façon de faire une demande de JSON est passe correct json comme 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));

+2

tnx. solution correcte dans asp.net mvc. –

+2

C'est la bonne solution, _period_ - tout le monde semble être mélangé à propos de 'x-www-form-urlencoded' vs' application/json', soit en les mélangeant, soit en double-enveloppant JSON dans une chaîne codée en url. –

10

J'ai créé une enveloppe mince chercher autour de() avec de nombreuses améliorations si vous utilisez un purement API REST 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 lavariables 0 et 4 méthodes:

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

Et dans une fonction async:

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

Je pense que vous vouliez dire un ensemble différent d'arguments à 'Object.assign'? devrait être 'Object.assign ({}, api.headers, headers)' car vous ne voulez pas continuer à ajouter des en-têtes personnalisés dans le hachage de 'api.headers' commun. droite? – Mobigital

+0

@Mobigital totalement droit, je ne savais pas à propos de cette nuance à l'époque, mais maintenant c'est la seule façon de le faire –

3

Ceci est lié à Content-Type. Comme vous avez pu le constater à la suite d'autres discussions et réponses à cette question, certaines personnes ont pu le résoudre en réglant 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 $.post() de jQuery et que cela fonctionne, la raison est probablement à cause de jQuery en utilisant 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'} 
}) 
+0

Mon backend dev construit l'API avec PHP, attendait les données à la chaîne de requête comme, pas un objet JSON . Cela a résolu la réponse vide du côté serveur. – eballeste

6

eu le même problème - pas body a été envoyé à partir d'un client à un serveur.

Ajout tête Content-Type a résolu le problème 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 => { 
    ... 
})