Recuperar: datos POST json


562

Estoy tratando de PUBLICAR un objeto JSON usando fetch .

Por lo que puedo entender, necesito adjuntar un objeto en cadena al cuerpo de la solicitud, por ejemplo:

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

Al usar el json echo de jsfiddle, esperaría ver el objeto que he enviado ( {a: 1, b: 2}) de regreso, pero esto no sucede: las herramientas de Chrome no muestran el JSON como parte de la solicitud, lo que significa que no se está enviando.


¿Qué navegador estás usando?
Krzysztof Safjanowski

@KrzysztofSafjanowski chrome 42, que debe tener soporte de recuperación total
Razor

compruebe este violín jsfiddle.net/abbpbah4/2 ¿qué datos espera? porque obtener la solicitud de fiddle.jshell.net/echo/json muestra un objeto vacío. {}
Kaushik

@KaushikKishore editado para aclarar el resultado esperado. res.json()Debería volver {a: 1, b: 2}.
Navaja

1
Olvidó incluir la jsonpropiedad que contiene los datos que desea enviar. Sin embargo, no bodyestoy siendo tratado correctamente de todos modos. Vea este violín para ver que se omite el retraso de 5 segundos. jsfiddle.net/99arsnkg Además, cuando intenta agregar encabezados adicionales, se ignoran. Este es probablemente un problema en fetch()sí mismo.
boombox

Respuestas:


599

Con el async/awaitsoporte ES2017 , así es como POSTuna carga útil 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);
})();

¿No puedes usar ES2017? Ver la respuesta de @ vp_art usando promesas

Sin embargo, la pregunta es pedir un problema causado por un error de Chrome solucionado hace mucho tiempo.
La respuesta original sigue.

Devtools de Chrome ni siquiera muestra el JSON como parte de la solicitud

Este es el problema real aquí , y es un error con las herramientas de Chrome, solucionado en Chrome 46.

Ese código funciona bien: está PUBLICANDO el JSON correctamente, simplemente no se puede ver.

Esperaría ver el objeto que he enviado de vuelta

eso no funciona porque ese no es el formato correcto para el eco de JSfiddle .

El código correcto es:

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

Para los puntos finales que aceptan cargas JSON, el código original es correcto


15
Para el registro, esto no está publicando una carga JSON: esta es una publicación de formulario ( x-www-form-urlencoded) con datos JSON en un campo llamado json. Entonces los datos están doblemente codificados. Para una publicación JSON limpia, vea la respuesta de @vp_arth a continuación.
mindplay.dk

1
@ mindplay.dk Esta no es una publicación x-www-form-urlencoded. Fetch API siempre usa codificación multiparte / datos de formulario en objetos FormData.
JukkaP

@JukkaP Estoy corregido. Mi punto principal fue el problema de la doble codificación.
mindplay.dk

2
Content-Type sigue siendo text / html; charset = iso-8859-1 no sé lo que estoy haciendo mal ...
KT Works

3
Para estar seguro, sería bueno confirmar res.oken caso de que el código de respuesta sea algún tipo de error. También sería bueno tener una .catch()cláusula al final. Me doy cuenta de que esto es solo un fragmento de muestra, pero tenga esto en cuenta para el uso en el mundo real.
Ken Lyon

207

Creo que su problema es solo jsfiddleprocesar la form-urlencodedsolicitud.

Pero la forma correcta de hacer una solicitud json es pasar correctamente jsoncomo un cuerpo:

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


66
Esta es la solución correcta, punto : todos los demás parecen estar confundidos acerca de x-www-form-urlencodedvs application/json, ya sea que no coincidan o que doblen JSON en cadenas codificadas en url.
mindplay.dk

Pero esto no funciona para jsfiddle. Por lo tanto, no estoy seguro de entender por qué diría "Esta es la solución correcta, punto". ¿No están todos los demás haciendo el ajuste para satisfacer la API de la /echoruta de jsfiddle ?
adam-beck

69

Desde los motores de búsqueda, terminé en este tema para publicar datos que no son de Json con fetch, así que pensé en agregar esto.

Para los que no son json , no tiene que usar datos de formulario. Simplemente puede establecer el Content-Typeencabezado application/x-www-form-urlencodedy usar una cadena:

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

Una forma alternativa de construir esa bodycadena, en lugar de escribirla como hice anteriormente, es usar bibliotecas. Por ejemplo, la stringifyfunción de query-stringo qspaquetes. Entonces usar esto se vería así:

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
muchas gracias por la cadena de consulta, lo intenté muchas veces con JSON.stringify pero ajax no devolvió la respuesta. pero la cadena de consulta hizo el truco. También descubrí que se debía a que fetch crea json para los parámetros del cuerpo en lugar de crear una cadena.
Danés

1
¡Gracias hombre! Esta es la mejor respuesta! Estuve golpeando la pared ayer durante unas horas tratando de encontrar una forma de enviar 'cuerpo' con datos de formulario desde mi aplicación web a mi servidor ... Una sugerencia: $ npm install cors --save Esto es necesario para deshacerse de " modo: 'no-cors' "en la solicitud Fetch, consulte github.com/expressjs/cors
Alexander Cherednichenko el

Gracias @AlexanderCherednichenko! Y gracias por compartir esa nota de cors que es una interesante que no conocía. :)
Noitidart

1
Gracias desde lo profundo de mi corazón. Me salvaste el tiempo y también mi vida dos veces :)
bafsar

1
Thnaks @bafsar!
Noitidart

42

Después de pasar algunas veces, realice ingeniería inversa jsFiddle, tratando de generar carga útil: hay un efecto.

Esté atento (en línea) return response.json();cuando la respuesta no sea una respuesta, es una promesa.

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


¿Por qué en su 'x-www-form-urlencodedlugar application/json? ¿Cual es la diferencia?
Juan Picado

@JuanPicado: después de la ingeniería inversa de jsfiddle hace 2 años, solo era una opción que podía funcionar. Por supuesto, application/jsones la forma correcta y funciona ahora. Gracias por un buen ojo
:)

yw Curioso detalle, me funciona a la antigua usanza con fetch( stackoverflow.com/questions/41984893/… ) en su lugar application/json. Quizás sabes por qué ...
Juan Picado

66
El Content-Typees application/json, pero su real bodyparece ser x-www-form-urlencoded- ¿No creo que esto debería funcionar? Si funciona, su servidor debe ser bastante indulgente. La respuesta de @vp_arth a continuación parece ser la correcta.
mindplay.dk

18

He creado una envoltura delgada alrededor de fetch () con muchas mejoras si está utilizando una 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);
});

Para usarlo tienes la variable apiy 4 métodos:

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

Y dentro de una asyncfunción:

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

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

Creo que te refieres a un conjunto diferente de argumentos para Object.assign? debería ser Object.assign({}, api.headers, headers)porque no quieres seguir agregando custom headersen hash de common api.headers. ¿Correcto?
Mobigital

@Mobigital tiene toda la razón, no sabía sobre ese matiz en ese momento, pero ahora es la única forma en que lo hago
Francisco Presencia el

11

Esto está relacionado con Content-Type. Como habrás notado en otras discusiones y respuestas a esta pregunta, algunas personas pudieron resolverlo configurando Content-Type: 'application/json'. Desafortunadamente en mi caso no funcionó, mi solicitud POST todavía estaba vacía en el lado del servidor.

Sin embargo, si prueba con jQuery's $.post()y está funcionando, la razón probablemente sea porque jQuery usa en Content-Type: 'x-www-form-urlencoded'lugar 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'}
})

1
Mi desarrollador de back-end construyó la API con PHP, esperaba que los datos fueran una cadena de consulta como, no un objeto json. Esto resolvió la respuesta vacía en el lado del servidor.
eballeste

11

Tuve el mismo problema: no bodyse envió un mensaje desde un cliente a un servidor.

Agregar Content-Typeencabezado lo resolvió para mí:

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 respuesta principal no funciona para PHP7, porque tiene una codificación incorrecta, pero podría encontrar la codificación correcta con las otras respuestas. Este código también envía cookies de autenticación, que probablemente desee cuando se trata, por ejemplo, de foros 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

Puede ser útil para alguien:

Estaba teniendo el problema de que los datos de formulario no se enviaban para mi solicitud

En mi caso, fue una combinación de los siguientes encabezados que también estaban causando el problema y el tipo de contenido incorrecto.

Entonces estaba enviando estos dos encabezados con la solicitud y no estaba enviando los datos de formulario cuando eliminé los encabezados que funcionaban.

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

Además, como otras respuestas sugieren que el encabezado Content-Type debe ser correcto.

Para mi solicitud, el encabezado correcto de Content-Type era:

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

En resumen, si sus datos de formulario no se adjuntan a la Solicitud, podrían ser sus encabezados. Intente llevar sus encabezados al mínimo y luego intente agregarlos uno por uno para ver si su problema está resuelto.


3

Creo que, no necesitamos analizar el objeto JSON en una cadena, si el servidor remoto acepta json en su solicitud, simplemente ejecute:

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

Tal como la solicitud de rizo

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

En caso de que el servicio remoto no acepte un archivo json como cuerpo, simplemente envíe un formulario de datos:

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

Tal como la solicitud de rizo

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

2
Esto es descaradamente incorrecto. No tiene nada que ver con el lado del servidor, ya sea que necesite o no encadenar su json. ¡Eso es exactamente lo que su curlcomando está haciendo implícitamente! Si no encadena sus objetos antes de pasarlos body, simplemente los enviará "[object Object]"como el cuerpo de su solicitud. Una simple prueba en Dev Tools te mostrará eso. Ábrelo e intenta esto sin salir de esta pestaña:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
oligofren

2

Si su carga útil JSON contiene matrices y objetos anidados, usaría URLSearchParams el param()método jQuery .

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

Para su servidor, esto se verá como un HTML estándar que se <form>está POSTeditando.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.