Pobierz: dane Json POST

561

Próbuję opublikować obiekt JSON przy użyciu funkcji pobierania .

Z tego, co rozumiem, muszę dołączyć obiekt strunowy do treści żądania, np .:

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

Podczas korzystania z echa jsfiddle'a jsfiddle spodziewałbym się zobaczyć obiekt, który wysłałem ( {a: 1, b: 2}) z powrotem, ale tak się nie dzieje - chrome devtools nawet nie pokazują JSON jako części żądania, co oznacza, że ​​nie jest wysyłany.

Brzytwa
źródło
Z jakiej przeglądarki korzystasz?
Krzysztof Safjanowski
@KrzysztofSafjanowski chrome 42, który ma mieć pełną obsługę pobierania
Razor
sprawdź to skrzypce jsfiddle.net/abbpbah4/2 jakich danych oczekujesz? ponieważ żądanie get fiddle.jshell.net/echo/json pokazuje pusty obiekt. {}
Kaushik,
@KaushikKishore edytowane w celu wyjaśnienia oczekiwanych wyników. res.json()powinien wrócić {a: 1, b: 2}.
Razor
1
Zapomniałeś dołączyć jsonwłaściwość zawierającą dane, które chcesz wysłać. Jednak i tak bodynie jest traktowany poprawnie. Zobacz to skrzypce, aby zobaczyć, że opóźnienie 5 sekund zostanie pominięte. jsfiddle.net/99arsnkg Również przy próbie dodania dodatkowych nagłówków są one ignorowane. Jest to prawdopodobnie problem fetch()sam w sobie.
boombox

Odpowiedzi:

597

Z ES2017 async/awaitwsparcia , to jak POSTładowności 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);
})();

Nie możesz użyć ES2017? Zobacz odpowiedź @ vp_art za pomocą obietnic

Pytanie dotyczy jednak problemu spowodowanego przez dawno naprawiony błąd chrome.
Oryginalna odpowiedź następuje.

chrome devtools nawet nie pokazuje JSON jako części żądania

To jest prawdziwy problem i jest to błąd w chrome devtools , naprawiony w Chrome 46.

Ten kod działa dobrze - poprawnie umieszcza JSON, po prostu nie można go zobaczyć.

Spodziewałbym się zobaczyć obiekt, który odesłałem

to nie działa, ponieważ nie jest to poprawny format echa JSfiddle .

Poprawny kod jest:

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

W przypadku punktów końcowych przyjmujących ładunki JSON oryginalny kod jest poprawny

Brzytwa
źródło
15
Dla przypomnienia, nie jest to przesyłanie ładunku JSON - jest to formularz post ( x-www-form-urlencoded) z danymi JSON w polu o nazwie json. Dane są więc podwójnie kodowane. Aby uzyskać czysty post JSON, zobacz odpowiedź autorstwa @vp_arth poniżej.
mindplay.dk
1
@ mindplay.dk To nie jest post x-www-form-urlencoded. Interfejs API Fetch zawsze używa kodowania danych wieloczęściowych / formularzy w obiektach FormData.
JukkaP
@JukkaP Stoję poprawiony. Moim głównym celem była kwestia podwójnego kodowania.
mindplay.dk
2
Content-Type to nadal text / html; charset = iso-8859-1 nie wiem, co robię źle ...
KT Działa
3
Aby być bezpiecznym, dobrze byłoby potwierdzić res.okw przypadku błędu w kodzie odpowiedzi. Dobrze byłoby też mieć .catch()klauzulę na końcu. Zdaję sobie sprawę, że to tylko fragment próbny, ale pamiętaj o tych rzeczach do użytku w świecie rzeczywistym.
Ken Lyon
206

Myślę, że Twój problem dotyczy jsfiddletylko przetwarzania form-urlencodedwniosków.

Ale poprawnym sposobem na przesłanie żądania json jest podanie poprawne jsonjako treści:

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
źródło
6
To poprawne rozwiązanie, kropka - wszyscy wydają się być pomyleni w stosunku do x-www-form-urlencodedvs application/json, albo nie pasując do nich, albo podwójnie owijając JSON ciągami kodowanymi w adresie URL.
mindplay.dk
Ale to nie działa dla jsfiddle. Nie jestem więc pewien, czy rozumiem, dlaczego powiedziałbyś „To jest właściwe rozwiązanie, kropka”. Czy nie wszyscy robią zawijanie, aby spełnić API trasy jsfiddle /echo?
adam-beck
69

Z wyszukiwarek skończyłem na tym temacie, że publikowanie danych spoza JSON z funkcją pobierania, więc pomyślałem, że mogę to dodać.

W przypadku non-json nie musisz używać danych formularza. Możesz po prostu ustawić Content-Typenagłówek application/x-www-form-urlencodedi użyć ciągu:

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

Alternatywnym sposobem na zbudowanie tego bodyciągu, zamiast wpisywania go tak, jak to zrobiłem powyżej, jest użycie bibliotek. Na przykład stringifyfunkcja z query-stringlub qspakiety. Używając tego, wyglądałoby to tak:

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
źródło
2
dziękuję bardzo za ciąg zapytania, tyle razy próbowałem z JSON.stringify, ale ajax nie zwracał odpowiedzi. ale ciąg zapytania zadziałał. Odkryłem również, że dzieje się tak, ponieważ pobieranie tworzy json dla parametrów ciała zamiast tworzenia łańcucha.
duński
1
Dziękuje! To najlepsza odpowiedź! Wczoraj uderzyłem w ścianę przez kilka godzin, próbując znaleźć sposób na przesłanie „ciała” z danymi formularza z mojej aplikacji internetowej na mój serwer ... Jedna sugestia: $ npm install cors --save To jest potrzebne, aby pozbyć się „ mode: „no-cors” ”w żądaniu pobierania
Alexander Cherednichenko
Dzięki @AlexanderCherednichenko! I dziękuję za udostępnienie tej notatki cors, która jest interesująca, o której nie wiedziałam. :)
Noitidart,
1
Dziękuję z głębi serca. Zaoszczędziłeś dwa razy mój czas i życie :)
bafsar
1
Thnaks @bafsar!
Noitidart
42

Po spędzeniu kilku chwil, inżynieria wsteczna jsFiddle, próba wygenerowania ładunku - jest efekt.

Proszę zwrócić uwagę (ostrożność) na linię, return response.json();gdzie reakcja nie jest odpowiedzią - jest obietnicą.

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 i& Chrome> 42

Krzysztof Safjanowski
źródło
Dlaczego 'x-www-form-urlencodedzamiast tego application/json? Co za różnica?
Juan Picado,
@JuanPicado - po inżynierii wstecznej jsfiddle 2 lata temu była to tylko jedna opcja, która mogła działać. Oczywiście application/jsonjest to poprawna forma i działa teraz. Dzięki za dobre oko
:)
yw. Ciekawe szczegóły, działa dla mnie po staremu z fetch( stackoverflow.com/questions/41984893/... ) zamiast application/json. Być może wiesz, dlaczego ...
Juan Picado
6
Tak Content-Typejest application/json, ale bodywydaje się, że tak jest x-www-form-urlencoded- nie sądzę, że to powinno działać? Jeśli to działa, twój serwer musi być bardzo wyrozumiały. Odpowiedź @vp_arth poniżej wydaje się być poprawna.
mindplay.dk,
18

Utworzyłem cienkie opakowanie wokół funkcji fetch () z wieloma ulepszeniami, jeśli używasz czysto json API REST:

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

Aby go użyć, masz zmienną apii 4 metody:

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

I w ramach asyncfunkcji:

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

Przykład z 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
źródło
Myślę, że miałeś na myśli inny zestaw argumentów Object.assign? powinno być Object.assign({}, api.headers, headers)tak, ponieważ nie chcesz ciągle dodawać niestandardowych wartości headersdo mieszania wspólnego api.headers. dobrze?
Mobigital
@Migigital całkowicie słusznie, wtedy nie wiedziałem o tym niuansie, ale teraz jest to jedyny sposób, w jaki to robię
Francisco Presencia
11

Jest to związane z Content-Type. Jak można zauważyć z innych dyskusji i odpowiedzi na to pytanie, niektórzy ludzie byli w stanie rozwiązać to przez ustawienieContent-Type: 'application/json' . Niestety w moim przypadku nie zadziałało, moje żądanie POST było nadal puste po stronie serwera.

Jeśli jednak spróbujesz użyć jQuery's $.post()i działa, przyczyną jest prawdopodobnie użycie jQuery Content-Type: 'x-www-form-urlencoded'zamiast 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
źródło
1
Mój backend dev zbudował API z PHP, spodziewał się, że dane będą ciągiem zapytania, a nie obiektem json. To rozwiązało pustą odpowiedź po stronie serwera.
eballeste
11

Miał ten sam problem - nie bodyzostał wysłany z klienta na serwer.

Dodanie Content-Typenagłówka rozwiązało to dla mnie:

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 => {
   ...
})
Zielony
źródło
7

Najlepsza odpowiedź nie działa dla PHP7, ponieważ ma złe kodowanie, ale mógłbym znaleźć właściwe kodowanie z innymi odpowiedziami. Ten kod wysyła również uwierzytelniające pliki cookie, które prawdopodobnie chcesz mieć do czynienia np. Z forami 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
źródło
3

Może to być przydatne dla kogoś:

Miałem problem, że formdata nie została wysłana dla mojej prośby

W moim przypadku przyczyną problemu był niewłaściwy typ treści i kombinacja następujących nagłówków.

Wysłałem więc te dwa nagłówki z żądaniem i nie wysyłałem danych formd, kiedy usunąłem działające nagłówki.

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

Również inne odpowiedzi sugerują, że nagłówek Content-Type musi być poprawny.

Dla mojego żądania poprawny nagłówek Content-Type to:

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

Tak więc, jeśli twoje dane formalne nie są dołączane do żądania, mogą to być twoje nagłówki. Spróbuj ograniczyć nagłówki do minimum, a następnie spróbuj dodawać je jeden po drugim, aby sprawdzić, czy problem został rozwiązany.

user_CC
źródło
3

Myślę, że nie potrzebujemy parsować obiektu JSON w ciąg, jeśli zdalny serwer akceptuje json w żądaniu, wystarczy uruchomić:

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

Takich jak żądanie zwijania

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

Jeśli zdalny serwer nie zaakceptuje pliku json jako treści, po prostu wyślij formularz danych:

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

Takich jak żądanie zwijania

curl -v -X POST -H 'Content-type: application/x-www-form-urlencoded' -d '@data.txt' '/echo/form'
Daniel García
źródło
2
Jest to rażąco niepoprawne. Nie ma to nic wspólnego ze stroną serwera, bez względu na to, czy musisz skorygować swój plik Json. Dokładnie to robi twoje curlpolecenie! Jeśli nie skreślisz swoich obiektów przed przekazaniem ich jako tych body, które wyślesz "[object Object]"jako treść żądania. Pokazałby to prosty test w Dev Tools. Otwórz i wypróbuj to, nie opuszczając tej zakładki:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })
oligofren
2

Jeśli twój ładunek JSON zawiera tablice i zagnieżdżone obiekty, URLSearchParams użyłbym param()metody jQuery .

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

Się z serwerem, będzie to wyglądać jak standardowy HTML <form>samopoczucia POSTed.

Eric Sellin
źródło