Ambil: POST json data

562

Saya mencoba POST objek JSON menggunakan fetch .

Dari apa yang bisa saya mengerti, saya perlu melampirkan objek yang dirender ke badan permintaan, misalnya:

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

Saat menggunakan gema jsfiddle's json, saya berharap melihat objek yang telah saya kirim ( {a: 1, b: 2}) kembali, tetapi ini tidak terjadi - chrome devtools bahkan tidak menampilkan JSON sebagai bagian dari permintaan, yang berarti bahwa itu tidak dikirim.

Pisau cukur
sumber
Browser apa yang Anda gunakan?
Krzysztof Safjanowski
@KrzysztofSafjanowski chrome 42, yang dimaksudkan untuk mendapatkan dukungan penuh
Razor
periksa jsfiddle.net/abbpbah4/2 ini biola data apa yang Anda harapkan? karena mendapatkan permintaan dari fiddle.jshell.net/echo/json menunjukkan objek kosong. {}
Kaushik
@ KaushikKishore diedit untuk mengklarifikasi hasil yang diharapkan. res.json()harus kembali {a: 1, b: 2}.
Razor
1
Anda lupa menyertakan jsonproperti yang berisi data yang ingin Anda kirim. Namun, saya bodytidak diperlakukan dengan benar pula. Lihat biola ini untuk melihat bahwa keterlambatan 5 detik akan dilewati. jsfiddle.net/99arsnkg Juga, ketika Anda mencoba menambahkan header tambahan, mereka diabaikan. Ini mungkin masalah dengan fetch()dirinya sendiri.
boombox

Jawaban:

598

Dengan async/awaitdukungan ES2017 , ini adalah cara pembayaran POSTJSON:

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

Tidak dapat menggunakan ES2017? Lihat jawaban @ vp_art menggunakan janji

Namun pertanyaannya adalah menanyakan masalah yang disebabkan oleh bug chrome yang telah diperbaiki sejak lama.
Jawaban asli berikut.

devtools chrome bahkan tidak menunjukkan JSON sebagai bagian dari permintaan

Ini adalah masalah sebenarnya di sini , dan ini adalah bug dengan devtools krom , diperbaiki di Chrome 46.

Kode itu berfungsi dengan baik - itu POSTING JSON dengan benar, itu tidak bisa dilihat.

Saya berharap melihat objek yang saya kirim kembali

itu tidak berfungsi karena itu bukan format yang benar untuk gema JSfiddle .

The kode yang benar adalah:

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

Untuk titik akhir menerima muatan JSON, kode asli sudah benar

Pisau cukur
sumber
15
Sebagai catatan, ini bukan mem-posting payload JSON - ini adalah form post ( x-www-form-urlencoded) dengan data JSON dalam bidang bernama json. Jadi data dikodekan ganda. Untuk posting JSON yang bersih, lihat jawaban oleh @vp_arth di bawah ini.
mindplay.dk
1
@ mindplay.dk Ini bukan posting x-www-form-urlencoded. Ambil API selalu menggunakan pengkodean multipart / formulir-data pada objek FormData.
JukkaP
@ JukkaP saya berdiri dikoreksi. Poin utama saya adalah masalah pengkodean ganda.
mindplay.dk
2
Jenis-Konten masih berupa teks / html; charset = iso-8859-1 tidak tahu apa yang saya lakukan salah ...
KT Works
3
Untuk berada di sisi yang aman, akan lebih baik untuk mengonfirmasi res.okjika kode respons adalah semacam kesalahan. Itu juga bagus untuk memiliki .catch()klausa di akhir. Saya menyadari ini hanya cuplikan contoh, tetapi ingatlah hal-hal ini untuk penggunaan dunia nyata.
Ken Lyon
206

Saya pikir masalah Anda hanya jsfiddledapat memproses form-urlencodedpermintaan.

Tetapi cara yang benar untuk membuat permintaan json dilewati dengan benar jsonsebagai sebuah badan:

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
sumber
6
Ini adalah solusi yang tepat, titik - semua orang tampaknya bingung tentang x-www-form-urlencodedvs application/json, baik ketidakcocokan mereka atau JSON pembungkus ganda dalam string yang disandikan dengan url.
mindplay.dk
Tapi ini tidak berhasil untuk jsfiddle. Jadi, saya tidak yakin saya mengerti mengapa Anda akan mengatakan "Ini adalah solusi yang tepat, titik". Bukankah semua orang melakukan pembungkus untuk memenuhi API /echorute jsfiddle ?
adam-beck
69

Dari mesin pencari, saya berakhir pada topik ini untuk data posting non-json dengan mengambil, jadi saya pikir saya akan menambahkan ini.

Untuk non-json, Anda tidak harus menggunakan data formulir. Anda cukup mengatur Content-Typetajuk application/x-www-form-urlencodeddan menggunakan 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'
});

Cara alternatif untuk membangun bodystring itu, daripada mengetiknya seperti yang saya lakukan di atas, adalah dengan menggunakan perpustakaan. Misalnya stringifyfungsi dari query-stringatau qspaket. Jadi menggunakan ini akan terlihat seperti:

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
sumber
2
terima kasih banyak untuk string kueri, saya mencoba berkali-kali dengan JSON.stringify tetapi ajax tidak mengembalikan respons. tetapi string kueri berhasil. Saya menemukan juga bahwa itu karena mengambil json untuk membuat params tubuh daripada membuat string.
Denmark
1
Terima kasih kawan! Ini jawaban terbaik! Saya memukul dinding kemarin selama beberapa jam mencoba menemukan cara untuk mengirim 'tubuh' dengan data formulir dari aplikasi web saya ke server saya ... Satu saran: $ npm install cors --save Ini diperlukan untuk menghilangkan " mode: 'no-cors' "dalam permintaan Fetch lihat github.com/expressjs/cors
Alexander Cherednichenko
Terima kasih @AlexanderCherednichenko! Dan terima kasih telah berbagi bahwa catatan kors itu yang menarik yang tidak saya ketahui. :)
Noitidart
1
Terima kasih dari lubuk hatiku. Anda menghemat waktu saya dan juga hidup saya dua kali :)
bafsar
1
Thnaks @bafsar!
Noitidart
42

Setelah menghabiskan beberapa waktu, balikkan jsFiddle, mencoba menghasilkan payload - ada efeknya.

Tolong tatap mata (hati-hati) pada saluran di return response.json();mana respons bukanlah respons - itu adalah janji.

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
sumber
Kenapa 'x-www-form-urlencodedsebaliknya application/json? Apa bedanya?
Juan Picado
@JuanPicado - setelah jsfiddle reverse engineering 2 tahun lalu, hanya satu opsi yang bisa digunakan. Tentu saja application/jsonbentuk yang benar dan berfungsi sekarang. Terima kasih untuk mata yang baik
:)
yw. Detail ingin tahu, ini bekerja untuk saya dengan cara lama dengan fetch( stackoverflow.com/questions/41984893/… ) sebagai gantinya application/json. Mungkin Anda tahu mengapa ...
Juan Picado
6
The Content-Typeadalah application/json, tetapi Anda sebenarnya bodymuncul untuk menjadi x-www-form-urlencoded- saya tidak berpikir ini harus bekerja? Jika berhasil, server Anda pasti cukup pemaaf. Jawaban oleh @vp_arth di bawah ini kelihatannya benar.
mindplay.dk
18

Saya telah membuat pembungkus tipis sekitar fetch () dengan banyak perbaikan jika Anda menggunakan API SISA murni 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);
});

Untuk menggunakannya, Anda memiliki variabel apidan 4 metode:

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

Dan dalam suatu asyncfungsi:

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

Contoh dengan 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
sumber
Saya pikir maksud Anda adalah serangkaian argumen yang berbeda Object.assign? seharusnya Object.assign({}, api.headers, headers)karena Anda tidak ingin terus menambahkan kustom headerske dalam hash yang umum api.headers. Baik?
Mobigital
@ Digital benar-benar benar, saya tidak tahu tentang nuansa itu dulu tapi sekarang satu-satunya cara saya melakukannya
Francisco Presencia
11

Ini terkait dengan Content-Type. Seperti yang mungkin telah Anda perhatikan dari diskusi lain dan jawaban untuk pertanyaan ini beberapa orang dapat menyelesaikannya dengan menetapkan Content-Type: 'application/json'. Sayangnya dalam kasus saya itu tidak berfungsi, permintaan POST saya masih kosong di sisi server.

Namun, jika Anda mencoba dengan jQuery $.post()dan berfungsi, alasannya mungkin karena jQuery menggunakan Content-Type: 'x-www-form-urlencoded'alih-alih 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
sumber
1
Dev backend saya membuat API dengan PHP, mengharapkan data berupa string kueri, bukan objek json. Ini menyelesaikan respons kosong di sisi server.
eballeste
11

Punya masalah yang sama - tidak ada bodyyang dikirim dari klien ke server.

Menambahkan Content-Typeheader menyelesaikannya untuk saya:

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

Jawaban teratas tidak berfungsi untuk PHP7, karena memiliki penyandian yang salah, tetapi saya dapat menemukan penyandian yang tepat dengan jawaban lainnya. Kode ini juga mengirimkan cookie otentikasi, yang mungkin Anda inginkan ketika berhadapan dengan misalnya 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);
    });
}
lama12345
sumber
3

Mungkin bermanfaat bagi seseorang:

Saya mengalami masalah bahwa formdata tidak dikirim untuk permintaan saya

Dalam kasus saya, kombinasi header berikut yang juga menyebabkan masalah dan Tipe Konten yang salah.

Jadi saya mengirim dua header ini dengan permintaan dan itu tidak mengirim formata ketika saya menghapus header yang berfungsi.

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

Juga sebagai jawaban lain menyarankan bahwa header Tipe Konten perlu benar.

Untuk permintaan saya, header Jenis Konten yang benar adalah:

"Tipe-Konten": "application / x-www-form-urlencoded; charset = UTF-8"

Jadi intinya jika formdata Anda tidak dilampirkan pada Permintaan maka itu berpotensi menjadi tajuk Anda. Coba bawa tajuk Anda seminimal mungkin dan kemudian coba tambahkan satu per satu untuk melihat apakah masalah Anda teratasi.

user_CC
sumber
3

Saya berpikir bahwa, kita tidak perlu mengurai objek JSON menjadi sebuah string, jika server jauh menerima json ke dalam permintaan mereka, jalankan saja:

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

Seperti permintaan ikal

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

Dalam hal untuk melayani jarak jauh tidak menerima file json sebagai badan, cukup kirim 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
});

Seperti permintaan ikal

curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '@data.txt' '/echo/form'
Daniel García
sumber
2
Ini benar-benar salah. Ini tidak ada hubungannya dengan sisi server apakah Anda perlu atau tidak mengikat json Anda. Itulah tepatnya yang dilakukan curlperintah Anda secara implisit! Jika Anda tidak menjernihkan objek Anda sebelum meneruskannya karena bodyAnda hanya akan mengirim "[object Object]"sebagai badan permintaan Anda. Tes sederhana di Dev Tools akan menunjukkan kepada Anda hal itu. Buka dan coba ini tanpa meninggalkan tab ini:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
oligofren
2

Jika muatan JSON Anda berisi array dan objek bersarang, saya akan menggunakan URLSearchParams dan param()metode jQuery .

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

Untuk server Anda, ini akan terlihat seperti HTML standar yang <form>sedang POSTdiedit.

Eric Sellin
sumber