Buscar: Dados POST json

562

Estou tentando postar um objeto JSON usando busca .

Pelo que entendi, preciso anexar um objeto stringificado ao corpo da solicitação, por exemplo:

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

Ao usar o json echo do jsfiddle, espero ver o objeto que enviei ( {a: 1, b: 2}) de volta, mas isso não acontece - o chrome devtools nem mostra o JSON como parte da solicitação, o que significa que ele não está sendo enviado.

Navalha
fonte
Qual navegador você está usando?
Krzysztof Safjanowski
@KrzysztofSafjanowski chrome 42, que deve ter suporte completo à busca
Razor
verifique este violino jsfiddle.net/abbpbah4/2 quais dados você está esperando? porque a solicitação get de fiddle.jshell.net/echo/json está mostrando um objeto vazio. {}
Kaushik
@KaushikKishore editado para esclarecer a saída esperada. res.json()deve retornar {a: 1, b: 2}.
Lâmina
1
Você esqueceu de incluir a jsonpropriedade que contém os dados que deseja enviar. No entanto, eu bodynão estou sendo tratado corretamente de qualquer maneira. Veja este violino para ver que o atraso de 5 segundos é pulado. jsfiddle.net/99arsnkg Além disso, quando você tenta adicionar cabeçalhos adicionais, eles são ignorados. Provavelmente, esse é um problema fetch().
boombox

Respostas:

598

Com o async/awaitsuporte do ES2017 , é assim que POSTuma 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);
})();

Não pode usar o ES2017? Veja a resposta de @ vp_art usando promessas

A pergunta, no entanto, está pedindo um problema causado por um bug do Chrome corrigido há muito tempo.
A resposta original segue.

O Chrome Devtools nem mostra o JSON como parte da solicitação

Esse é o problema real aqui , e é um bug com as devtools do chrome , corrigidas no Chrome 46.

Esse código funciona bem - ele está postando o JSON corretamente, simplesmente não pode ser visto.

Eu esperaria ver o objeto que enviei de volta

isso não está funcionando porque esse não é o formato correto para o eco do JSfiddle .

O código correto é:

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 terminais que aceitam cargas JSON, o código original está correto

Navalha
fonte
15
Para o registro, isso não está postando uma carga útil JSON - esta é uma postagem de formulário ( x-www-form-urlencoded) com dados JSON em um campo chamado json. Portanto, os dados são duplamente codificados. Para uma postagem JSON limpa, consulte a resposta por @vp_arth abaixo.
mindplay.dk
1
@ mindplay.dk Esta não é uma publicação com código x-www-form-url. A API de busca sempre usa codificação multipart / form-data em objetos FormData.
JukkaP
@JukkaP Estou corrigido. Meu ponto principal foi a questão da codificação dupla.
mindplay.dk
2
O tipo de conteúdo ainda é text / html; charset = iso-8859-1 não sei o que estou fazendo de errado ...
KT Works
3
Para garantir a segurança, seria bom confirmar se res.oko código de resposta é algum tipo de erro. Também seria bom ter uma .catch()cláusula no final. Sei que esse é apenas um trecho de amostra, mas lembre-se disso para uso no mundo real.
Ken Lyon
206

Acho que seu problema jsfiddlepode processar form-urlencodedapenas solicitações.

Mas a maneira correta de fazer a solicitação json é passar correta jsoncomo um 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));

vp_arth
fonte
6
Esta é a solução correta, ponto final - todo mundo parece estar confuso sobre o x-www-form-urlencodedvs application/json, combinando-os incorretamente ou envolvendo o JSON duas vezes em uma string codificada por URL.
mindplay.dk
Mas isso não funciona para o jsfiddle. Portanto, não sei ao certo por que você diria "Esta é a solução correta, ponto final". Todos os outros não estão fazendo o encapsulamento para satisfazer a API da /echorota do jsfiddle ?
adam-beck
69

Nos mecanismos de pesquisa, acabei abordando este tópico para publicar dados não-json com busca, então pensei em adicionar isso.

Para não-json, você não precisa usar dados do formulário. Você pode simplesmente definir o Content-Typecabeçalho application/x-www-form-urlencodede usar uma 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: 'foo=bar&blah=1'
});

Uma maneira alternativa de criar essa bodystring, em vez de digitá-la como fiz acima, é usar bibliotecas. Por exemplo, a stringifyfunção de query-stringou qspacotes. Então, usando isso, ficaria assim:

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
fonte
2
muito obrigado pela string de consulta, tentei várias vezes com JSON.stringify, mas o ajax não estava retornando resposta. mas a string de consulta fez o truque. Descobri também que era porque buscar create json para parâmetros de corpo em vez de criar uma string.
Dinamarquês
1
Obrigado cara! Esta é a melhor resposta! Ontem, estive no muro por algumas horas, tentando encontrar uma maneira de enviar 'corpo' com dados do formulário do meu aplicativo da web para o meu servidor ... Uma sugestão: $ npm install cors --save Isso é necessário para se livrar de " mode: 'no-cors' "na solicitação Fetch, consulte github.com/expressjs/cors
Alexander Cherednichenko -
Obrigado @AlexanderCherednichenko! E obrigado por compartilhar essa nota importante que eu não conhecia. :)
Noitidart 6/07
1
Obrigado do fundo do meu coração. Você salvou o meu tempo e também a minha vida duas vezes :)
bafsar
1
Thnaks @bafsar!
Noitidart 23/10/19
42

Depois de passar algum tempo, faça a engenharia reversa do jsFiddle, tentando gerar carga útil - há um efeito.

Por favor, preste atenção na linha return response.json();onde a resposta não é uma resposta - é 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

Krzysztof Safjanowski
fonte
Por que em 'x-www-form-urlencodedvez disso application/json? Qual é a diferença?
Juan Picado 02/02
@JuanPicado - após a engenharia reversa da jsfiddle, há 2 anos, era apenas uma opção que poderia funcionar. Claro que application/jsoné a forma correta e funciona agora. Obrigado por olho bom:)
Krzysztof Safjanowski
yw. Detalhe curioso, ele funciona para mim da maneira antiga com fetch( stackoverflow.com/questions/41984893/… ) em vez de application/json. Talvez você sabe por quê ...
Juan Picado
6
A Content-Typeé application/json, mas seus reais bodyparece ser x-www-form-urlencoded- eu não acho que isso deve funcionar? Se funcionar, seu servidor deve ser bastante tolerante. A resposta de @vp_arth abaixo parece ser a correta.
mindplay.dk
18

Eu criei um invólucro fino em torno de fetch () com muitas melhorias se você estiver usando uma 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 usá-lo, você tem a variável apie 4 métodos:

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

E dentro de uma asyncfunção:

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

Exemplo com 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
fonte
Eu acho que você quis dizer um conjunto diferente de argumentos Object.assign? deve ser Object.assign({}, api.headers, headers)porque você não deseja continuar adicionando personalizado headersao hash comum api.headers. direita?
Mobigital
@Mobigital totalmente certo, eu não sei nada sobre isso nuance naquela época, mas agora é a única maneira que eu fazê-lo
Francisco Presencia
11

Isso está relacionado a Content-Type. Como você deve ter notado em outras discussões e respostas a essa pergunta, algumas pessoas foram capazes de resolvê-la definindo Content-Type: 'application/json'. Infelizmente, no meu caso, não funcionou, minha solicitação POST ainda estava vazia no lado do servidor.

No entanto, se você tentar com o jQuery $.post()e ele estiver funcionando, provavelmente o motivo é o uso do jQuery em Content-Type: 'x-www-form-urlencoded'vez 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
fonte
1
Meu desenvolvedor de back-end construiu a API com PHP, esperava que os dados fossem string de consulta como, não um objeto json. Isso resolveu a resposta vazia no lado do servidor.
eballeste
11

Teve o mesmo problema - não bodyfoi enviado de um cliente para um servidor.

Adicionar Content-Typecabeçalho resolveu isso para mim:

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

A resposta principal não funciona no PHP7, porque possui codificação incorreta, mas eu poderia descobrir a codificação correta com as outras respostas. Este código também envia cookies de autenticação, que você provavelmente deseja ao lidar com, por exemplo, fóruns 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
fonte
3

Pode ser útil para alguém:

Eu estava com o problema de que os dados do formulário não estavam sendo enviados para minha solicitação

No meu caso, foi uma combinação dos seguintes cabeçalhos que também estavam causando o problema e o Tipo de Conteúdo errado.

Então, eu estava enviando esses dois cabeçalhos com a solicitação e não estava enviando os dados do formulário quando removi os cabeçalhos que funcionavam.

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

Além disso, como outras respostas sugerem que o cabeçalho do tipo de conteúdo precisa estar correto.

Para minha solicitação, o cabeçalho correto do tipo de conteúdo era:

"Tipo de conteúdo": "application / x-www-form-urlencoded; charset = UTF-8"

Portanto, se os dados do formulário não estiverem sendo anexados à solicitação, podem ser seus cabeçalhos. Tente reduzir os cabeçalhos ao mínimo e, em seguida, adicione-os um por um para ver se o problema está resolvido.

user_CC
fonte
3

Eu acho que, não precisamos analisar o objeto JSON em uma string, se o servidor remoto aceitar o json no pedido, basta executar:

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

Como o pedido de ondulação

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

Caso o servidor remoto não aceite um arquivo json como corpo, basta enviar um 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
});

Como o pedido de ondulação

curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '@data.txt' '/echo/form'
Daniel García
fonte
2
Isso é flagrantemente incorreto. Não tem nada a ver com o lado do servidor, se você precisa ou não restringir seu json. É exatamente isso que seu curlcomando está fazendo implicitamente! Se você não especificar seus objetos antes de passá-los, bodyvocê apenas enviará "[object Object]"como o corpo da sua solicitação. Um teste simples no Dev Tools mostraria isso. Abra-o e tente isso sem sair desta guia:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
oligofren 6/06
2

Se sua carga útil JSON contiver matrizes e objetos aninhados, eu usaria URLSearchParams o param()método jQuery .

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

Para o seu servidor, isso parecerá um HTML padrão <form>sendo POSTeditado.

Eric Sellin
fonte