Вилучення: дані POST json


561

Я намагаюсь розмістити об'єкт JSON за допомогою добування .

З того, що я можу зрозуміти, мені потрібно приєднати строковий об'єкт до тіла запиту, наприклад:

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

Під час використання ехо jsfiddle json я очікував би побачити об'єкт, який я надіслав ( {a: 1, b: 2}), але цього не відбувається - хромовані devtools навіть не показують JSON як частину запиту, це означає, що він не надсилається.


Який браузер ви використовуєте?
Кшиштоф Саф'яновський

@KrzysztofSafjanowski chrome 42, який повинен мати повну підтримку
Razor

перевірте цю загадку jsfiddle.net/abbpbah4/2, які дані ви очікуєте? тому що отримання запиту fiddle.jshell.net/echo/json відображає порожній об’єкт. {}
Каушик

@KaushikKishore відредаговано, щоб уточнити очікуваний результат. res.json()повинен повернутися {a: 1, b: 2}.
Бритва

1
Ви забули включити jsonресурс, який містить дані, які ви хочете надіслати. Однак я так bodyчи інакше не лікується правильно. Дивіться цю загадку, щоб побачити, що затримка на 5 секунд пропускається. jsfiddle.net/99arsnkg Також при спробі додати додаткові заголовки вони ігноруються. Це, мабуть, проблема fetch()сама по собі.
бумбокс

Відповіді:


597

Завдяки async/awaitпідтримці ES2017 , це POSTкорисна навантаження 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);
})();

Не можете використовувати ES2017? Дивіться відповідь @ vp_art, використовуючи обіцянки

Однак питання задає питання, викликане давно виправленою хромовою помилкою.
Оригінальна відповідь випливає.

chrome devtools навіть не показує JSON як частину запиту

Тут справжня проблема , і це помилка з хромованими верстатами , виправлена ​​в Chrome 46.

Цей код працює чудово - він розміщує JSON правильно, його просто неможливо побачити.

Я очікував би побачити об’єкт, який я відправив назад

це не працює, тому що це не правильний формат для відлуння JSfiddle .

Правильний код є:

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

Для кінцевих точок, які приймають навантаження JSON, оригінальний код правильний


15
Для запису це не публікує корисну навантаження JSON - це форма форми ( x-www-form-urlencoded) з даними JSON у полі з назвою json. Таким чином, дані подвоюються вдвічі. Для отримання чистого допису JSON див. Відповідь @vp_arth нижче.
mindplay.dk

1
@ mindplay.dk Це не публікація з x-www-form-urlencoded. API Fetch завжди використовує кодування багаточастинних / форм-даних на об'єктах FormData.
JukkaP

@JukkaP Я стою виправлений. Головним моїм питанням було питання подвійного кодування.
mindplay.dk

2
Content-Type - це все-таки текст / html; charset = iso-8859-1 не знаю, що я роблю неправильно ...
KT Працює

3
Щоб бути в безпеці, було б добре підтвердити, res.okякщо код відповіді є якоюсь помилкою. Також було б добре мати .catch()підсумкове слово в кінці. Я розумію, що це лише зразок фрагмента, але майте на увазі ці речі для використання в реальному світі.
Кен Ліон

206

Я думаю, що ваша проблема полягає лише в jsfiddleобробці form-urlencodedзапиту.

Але правильний спосіб зробити запит json - це передати правильність jsonяк тіло:

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
Це правильне рішення, період - всі інші, здається, змішані щодо x-www-form-urlencodedvs application/json, або невідповідність їм, або подвійне загортання JSON у рядках, кодованих URL-адресою.
mindplay.dk

Але це не працює для jsfiddle. Отже, я не впевнений, я розумію, чому ви сказали б "Це правильне рішення, період". Чи не всі інші роблять обгортку, щоб задовольнити API /echoмаршруту jsfiddle ?
Адам-бек

69

З пошукових систем я закінчив цю тему для публікації даних, що не json, із завантаженням, тому подумав, що я додам це.

Для не-json не потрібно використовувати дані форми. Ви можете просто встановити Content-Typeзаголовок application/x-www-form-urlencodedі використовувати рядок:

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

Альтернативний спосіб побудови цього bodyрядка, а не введення його, як я робив вище, - використання бібліотек. Наприклад stringifyфункція від query-stringабо qsпакети. Отже, використовуючи це, це виглядатиме так:

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
велике дякую за рядок запиту, я так багато разів намагався з JSON.stringify, але ajax не повертав відповіді. але рядок запиту зробив свою справу. Я також виявив, що це тому, що вибираєте створення json для парами body замість створення рядка.
Датська

1
Спасибі, чоловіче! Це найкраща відповідь! Я вчора кілька годин бив об стіну, намагаючись знайти спосіб надіслати "тіло" даними форми з мого веб-додатку на мій сервер ... Одна пропозиція: $ npm встановити cors - зберегти Це потрібно для позбавлення " режим: 'no-cors' "у запиті Fetch див. github.com/expressjs/cors
Олександр Чередніченко

Дякую @AlexanderCherednichenko! І дякую за те, що поділилися, що колеги відзначають цікавий, про який я не знав. :)
Noitidart

1
Дякую від глибокого серця. Ви врятували мій час, а також моє життя двічі :)
bafsar

1
Thnaks @bafsar!
Noitidart

42

Провівши кілька разів, зворотний інжиніринг jsFiddle, намагаючись генерувати корисну навантаження - є ефект.

Будь ласка, зверніть увагу на те, return response.json();де відповідь не є відповіддю - це обіцянка.

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


Чому 'x-www-form-urlencodedзамість цього application/json? Яка різниця?
Хуан Пікадо

@JuanPicado - після jsfiddle зворотної інженерії 2 роки тому це був лише один варіант, який міг би працювати. Звичайно, application/jsonце правильна форма, і вона працює зараз. Дякую за добре око
:)

тис. Цікава деталь, вона працює для мене по-старому з fetch( stackoverflow.com/questions/41984893/… ) замість application/json. Можливо, ви знаєте, чому ...
Juan Picado

6
Content-TypeЄ application/json, але фактичні , bodyздається, x-www-form-urlencoded- я не думаю , що це повинно працювати? Якщо це все-таки працює, ваш сервер повинен дуже прощати. Відповідь від @vp_arth нижче, здається, є правильною.
mindplay.dk

18

Я створив тонку обгортку навколо fetch () з багатьма вдосконаленнями, якщо ви використовуєте суто json REST API:

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

Для його використання у вас є змінна apiта 4 методи:

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

І в межах asyncфункції:

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

Приклад з 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');
});

Я думаю, ти мав на увазі інший набір аргументів Object.assign? повинно бути, Object.assign({}, api.headers, headers)тому що ви не хочете продовжувати додавати звичайний headersхеш-код api.headers. правильно?
Mobigital

@Mobigital абсолютно прав, тоді я не знав про цей нюанс, але зараз це єдиний спосіб
Франциско Презенсія

11

Це пов’язано з Content-Type. Як ви могли помітити з інших дискусій та відповідей на це питання, деякі люди змогли вирішити це, встановивши Content-Type: 'application/json'. На жаль, у моєму випадку це не спрацювало, на моєму сервері запит POST все ще був порожнім.

Однак якщо ви спробуєте з jQuery $.post()і він працює, причина, ймовірно, в тому, що Content-Type: 'x-www-form-urlencoded'замість цього використовується jQuery 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
Мій бекенд-розробник розробив API з PHP, очікував, що дані будуть такими як рядки запиту, а не об’єктом json. Це вирішило порожню відповідь на стороні сервера.
eballeste

11

Був такий самий випуск - жоден bodyклієнт не надсилався на сервер.

Додавання Content-Typeзаголовка вирішило це для мене:

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

Верхня відповідь не працює для PHP7, оскільки вона має неправильне кодування, але я міг зрозуміти правильне кодування з іншими відповідями. Цей код також надсилає файли cookie аутентифікації, які ви, мабуть, хочете, маючи справу, наприклад, з форумами 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

Можливо, комусь це стане в нагоді:

У мене виникло питання, що формадані не надсилаються для мого запиту

У моєму випадку це була комбінація наступних заголовків, які також викликали проблему та неправильний тип вмісту.

Тож я надсилав ці два заголовки із запитом, і він не надсилав форматні дані, коли я видаляв заголовки, які працювали.

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

Крім того, як інші відповіді говорять про те, що заголовок Content-Type повинен бути правильним.

На мій запит правильний заголовок типу вмісту:

"Тип вмісту": "application / x-www-form-urlencoded; charset = UTF-8"

Отже, підсумок, якщо ваші форматні дані не додаються до запиту, вони потенційно можуть бути вашими заголовками. Спробуйте звести заголовки до мінімуму, а потім спробуйте додати їх по черзі, щоб побачити, чи вирішена ваша проблема.


3

Я думаю, що нам не потрібно розбирати об'єкт JSON в рядок, якщо віддалений сервер приймає json в їх запит, просто запустіть:

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

Такі як запит на завивку

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

Якщо вилучена служба не приймає файл json як тіло, просто надішліть форму даних:

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

Такі як запит на завивку

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

2
Це відверто неправильно. Це не має нічого спільного з сервером, незалежно від того, чи потрібно вам впорядкувати свій json. Це саме те, що ваша curlкоманда робить неявно! Якщо ви не впорядкуєте свої об'єкти перед тим, як передавати їх як bodyви, ви просто надішлете "[object Object]"як основний запит. Простий тест у програмах Dev Tools показав би вам це. Відкрийте його і спробуйте це, не виходячи з цієї вкладки:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
oligofren

2

Якщо ваша корисна навантаження JSON містить масиви та вкладені об'єкти, я використовував би метод URLSearchParams jQuery param().

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

Для вашого сервера це виглядатиме як стандартний HTML, <form>що POSTредагується.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.