Tìm nạp: POST dữ liệu json


562

Tôi đang cố gắng để gửi một đối tượng JSON bằng cách tìm nạp .

Từ những gì tôi có thể hiểu, tôi cần đính kèm một đối tượng được xâu chuỗi vào phần thân của yêu cầu, ví dụ:

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

Khi sử dụng tiếng vang json của jsfiddle, tôi mong muốn thấy đối tượng tôi đã gửi lại ( {a: 1, b: 2}), nhưng điều này không xảy ra - chrome devtools thậm chí không hiển thị JSON như một phần của yêu cầu, có nghĩa là nó không được gửi.


Bạn đang dùng trình duyệt nào?
Krzysztof Safjanowski

@KrzysztofSafjanowski chrome 42, có nghĩa là có hỗ trợ tìm nạp đầy đủ
Dao cạo

kiểm tra fiddle jsfiddle.net/abbpbah4/2 này bạn đang mong đợi dữ liệu gì? bởi vì nhận được yêu cầu của fiddle.jshell.net/echo/json đang hiển thị đối tượng trống. {}
Kaushik

@KaushikKishore chỉnh sửa để làm rõ đầu ra dự kiến. res.json()nên trở về {a: 1, b: 2}.
Dao cạo

1
Bạn đã quên bao gồm jsontài sản chứa dữ liệu bạn muốn gửi. Tuy nhiên, dù sao tôi bodycũng không được đối xử đúng. Xem câu đố này để thấy rằng độ trễ 5 giây bị bỏ qua. jsfiddle.net/99arsnkg Ngoài ra, khi bạn cố gắng thêm các tiêu đề bổ sung, chúng sẽ bị bỏ qua. Đây có lẽ là một vấn đề với fetch()chính nó.
boombox

Câu trả lời:


598

Với async/awaithỗ trợ ES2017 , đây là cách POSTtải trọng 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);
})();

Không thể sử dụng ES2017? Xem câu trả lời của @ vp_art bằng lời hứa

Tuy nhiên, câu hỏi đặt ra là vấn đề gây ra bởi lỗi chrome đã được sửa từ lâu.
Câu trả lời gốc sau đây.

chrome devtools thậm chí không hiển thị JSON như một phần của yêu cầu

Đây là vấn đề thực sự ở đây và đó là lỗi với chrome devtools , đã được sửa trong Chrome 46.

Mã đó hoạt động tốt - nó đang gửi JSON chính xác, nó không thể được nhìn thấy.

Tôi mong đợi được nhìn thấy đối tượng tôi đã gửi lại

điều đó không hoạt động vì đó không phải là định dạng chính xác cho tiếng vang của JSfiddle .

Các mã đúng là:

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

Đối với các điểm cuối chấp nhận tải trọng JSON, mã gốc là chính xác


15
Đối với bản ghi, đây không phải là đăng tải tải JSON - đây là bài đăng mẫu ( x-www-form-urlencoded) với dữ liệu JSON trong trường có tên json. Vì vậy, dữ liệu được mã hóa gấp đôi. Đối với một bài viết JSON sạch, xem câu trả lời của @vp_arth bên dưới.
mindplay.dk

1
@ mindplay.dk Đây không phải là một bài đăng x-www-form-urlencoding. API tìm nạp luôn sử dụng mã hóa đa dữ liệu / biểu mẫu trên các đối tượng FormData.
JukkaP

@JukkaP Tôi đứng sửa. Điểm chính của tôi là vấn đề mã hóa kép.
mindplay.dk

2
Kiểu nội dung vẫn là văn bản / html; charset = iso-8859-1 không biết tôi đang làm gì sai ...
KT Works

3
Để đảm bảo an toàn, sẽ tốt hơn nếu xác nhận res.oktrong trường hợp mã phản hồi là một loại lỗi. .catch()Cuối cùng cũng có một điều khoản tốt. Tôi nhận ra đây chỉ là một đoạn mẫu, nhưng hãy ghi nhớ những điều này để sử dụng trong thế giới thực.
Ken Lyon

206

Tôi nghĩ vấn đề của bạn chỉ jsfiddlecó thể xử lý form-urlencodedyêu cầu.

Nhưng cách chính xác để thực hiện yêu cầu json là vượt qua chính xác jsonnhư một cơ thể:

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


6
Đây là giải pháp chính xác, thời gian - mọi người khác dường như bị lẫn lộn về x-www-form-urlencodedvs application/json, hoặc không khớp chúng hoặc gói hai lần JSON trong một chuỗi được mã hóa url.
mindplay.dk

Nhưng điều này không làm việc cho jsfiddle. Vì vậy, tôi không chắc chắn tôi hiểu lý do tại sao bạn sẽ nói "Đây là giải pháp chính xác, thời gian". Không phải ai khác cũng đang thực hiện gói để đáp ứng API của /echotuyến đường của jsfiddle sao?
adam-beck

69

Từ các công cụ tìm kiếm, tôi đã kết thúc chủ đề này để đăng dữ liệu không phải là json với tìm nạp, vì vậy tôi nghĩ rằng tôi sẽ thêm nó.

Đối với non-json, bạn không phải sử dụng dữ liệu biểu mẫu. Bạn có thể chỉ cần đặt Content-Typetiêu đề thành application/x-www-form-urlencodedvà sử dụng một chuỗi:

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

Một cách khác để xây dựng bodychuỗi đó , thay vì gõ nó như tôi đã làm ở trên, là sử dụng các thư viện. Ví dụ, stringifychức năng từ query-stringhoặc qsgói. Vì vậy, sử dụng nó sẽ trông như:

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
cảm ơn bạn rất nhiều về chuỗi truy vấn, tôi đã thử rất nhiều lần với JSON.opesify nhưng ajax không trả lời phản hồi. nhưng chuỗi truy vấn đã lừa Tôi cũng thấy rằng đó là vì tìm nạp json cho các thông số cơ thể thay vì tạo một chuỗi.
Đan Mạch

1
Cảm ơn bạn! Đây là câu trả lời tốt nhất! Tôi đã va vào tường ngày hôm qua trong vài giờ cố gắng tìm cách gửi 'cơ thể' với dữ liệu biểu mẫu từ ứng dụng web của mình đến máy chủ của mình ... Một đề xuất: $ npm install cors --save Điều này là cần thiết để thoát khỏi " chế độ: 'no-cors' "trong yêu cầu Tìm nạp, xem github.com/expressjs/cors
Alexander Cherednichenko

Cảm ơn @AlexanderCherednichenko! Và cảm ơn vì đã chia sẻ rằng cors lưu ý rằng đó là một điều thú vị mà tôi không biết. :)
Noitidart

1
Cảm ơn từ sâu thẳm trái tim tôi. Bạn đã tiết kiệm thời gian của tôi và cuộc sống của tôi hai lần :)
bafsar

1
Thnaks @bafsar!
Noitidart

42

Sau khi dành một số lần, jsFiddle kỹ thuật đảo ngược, cố gắng tạo ra tải trọng - có một hiệu ứng.

Xin hãy chú ý trực tuyến return response.json();nơi phản hồi không phải là phản hồi - đó là lời hứa.

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


Tại sao 'x-www-form-urlencodedthay thế application/json? Có gì khác biệt?
Juan Picado

@JuanPicado - sau khi jsfiddle kỹ thuật đảo ngược 2 năm trước, nó chỉ là một lựa chọn mà nó có thể hoạt động. Tất nhiên application/jsonlà hình thức chính xác và nó hoạt động bây giờ. Cảm ơn vì mắt tốt :)
Krzysztof Safjanowski

vâng Tò mò chi tiết, nó hoạt động với tôi theo cách cũ với fetch( stackoverflow.com/questions/41984893/ mẹo ) thay vì application/json. Có lẽ bạn biết tại sao ...
Juan Picado

6
Các Content-Typeapplication/json, nhưng thực tế của bạn bodydường như là x-www-form-urlencoded- Tôi không nghĩ rằng điều này sẽ làm việc? Nếu nó hoạt động, máy chủ của bạn phải được tha thứ. Câu trả lời của @vp_arth dưới đây có vẻ là câu trả lời đúng.
mindplay.dk

18

Tôi đã tạo một trình bao bọc mỏng xung quanh fetch () với nhiều cải tiến nếu bạn đang sử dụng API REST hoàn toàn 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);
});

Để sử dụng nó, bạn có biến apivà 4 phương thức:

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

Và trong một asyncchức năng:

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

Ví dụ với 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');
});

Tôi nghĩ bạn có nghĩa là một tập hợp các đối số khác nhau Object.assign? nên Object.assign({}, api.headers, headers)bởi vì bạn không muốn tiếp tục thêm tùy chỉnh headersvào hàm băm phổ biến api.headers. đúng?
Mobigital

@Mobigital hoàn toàn đúng, lúc đó tôi không biết về sắc thái đó nhưng bây giờ đó là cách duy nhất tôi làm
Francisco Presencia

11

Điều này có liên quan đến Content-Type. Như bạn có thể nhận thấy từ các cuộc thảo luận và câu trả lời khác cho câu hỏi này, một số người có thể giải quyết nó bằng cách cài đặt Content-Type: 'application/json'. Thật không may trong trường hợp của tôi nó không hoạt động, yêu cầu POST của tôi vẫn trống ở phía máy chủ.

Tuy nhiên, nếu bạn thử với jQuery $.post()và nó hoạt động, lý do có thể là do jQuery sử dụng Content-Type: 'x-www-form-urlencoded'thay vì 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
Nhà phát triển phụ trợ của tôi đã xây dựng API bằng PHP, hy vọng dữ liệu sẽ là chuỗi truy vấn, không phải là đối tượng json. Điều này đã giải quyết phản hồi trống ở phía máy chủ.
eballeste

11

Có cùng một vấn đề - không bodyđược gửi từ máy khách đến máy chủ.

Thêm Content-Typetiêu đề đã giải quyết nó cho tôi:

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

Câu trả lời hàng đầu không hoạt động đối với PHP7, vì nó có mã hóa sai, nhưng tôi có thể tìm ra mã hóa đúng với các câu trả lời khác. Mã này cũng gửi cookie xác thực mà bạn có thể muốn khi giao dịch với các diễn đàn 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

Nó có thể hữu ích cho ai đó:

Tôi gặp vấn đề là formdata không được gửi cho yêu cầu của tôi

Trong trường hợp của tôi, đó là sự kết hợp của các tiêu đề sau cũng gây ra sự cố và Loại nội dung sai.

Vì vậy, tôi đã gửi hai tiêu đề này với yêu cầu và nó đã không gửi formdata khi tôi loại bỏ các tiêu đề hoạt động.

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

Ngoài ra, như các câu trả lời khác cho thấy tiêu đề Kiểu Nội dung cần phải chính xác.

Đối với yêu cầu của tôi, tiêu đề Kiểu nội dung chính xác là:

"Loại nội dung": "application / x-www-form-urlencoding; charset = UTF-8"

Vì vậy, dòng dưới cùng nếu formdata của bạn không được đính kèm với Yêu cầu thì nó có thể có khả năng là tiêu đề của bạn. Hãy thử đưa tiêu đề của bạn đến mức tối thiểu và sau đó thử thêm từng cái một để xem vấn đề của bạn đã được giải quyết chưa.


3

Tôi nghĩ rằng, chúng ta không cần phân tích đối tượng JSON thành một chuỗi, nếu máy chủ từ xa chấp nhận json vào chúng yêu cầu, chỉ cần chạy:

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

Chẳng hạn như yêu cầu curl

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

Trong trường hợp phục vụ từ xa không chấp nhận tệp json làm phần thân, chỉ cần gửi 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
});

Chẳng hạn như yêu cầu curl

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

2
Điều này là không chính xác. Nó không liên quan gì đến phía máy chủ cho dù bạn có cần xâu chuỗi json của mình hay không. Đó chính xác là những gì curllệnh của bạn đang làm ngầm! Nếu bạn không xâu chuỗi các đối tượng của mình trước khi chuyển chúng như bodybạn sẽ gửi "[object Object]"như là phần thân của yêu cầu của bạn. Một thử nghiệm đơn giản trong Dev Tools sẽ cho bạn thấy điều đó. Mở nó lên và thử cái này mà không cần rời khỏi tab này:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
oligofren

2

Nếu tải trọng JSON của bạn chứa các mảng và các đối tượng lồng nhau, tôi sẽ sử dụng URLSearchParamsparam()phương thức của jQuery .

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

Đối với máy chủ của bạn, nó sẽ trông giống như một HTML tiêu chuẩn <form>đang được chỉnh sửa POST.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.