关于javascript:fetch:post-json数据

Fetch: POST json data

我尝试使用fetch发布一个JSON对象。

据我所知,我需要在请求的主体上附加一个字符串化的对象,例如:

1
2
3
4
5
6
7
8
9
10
11
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 echo时,我希望看到我发送的对象({a: 1, b: 2})返回,但这不会发生-chrome devtools甚至不将json作为请求的一部分显示,这意味着它不会被发送。


有了ES2017 async/await支持,这就是如何使用POST一个JSON负载:

1
2
3
4
5
6
7
8
9
10
11
12
13
(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?使用承诺查看@vpart的答案

然而,这个问题要求解决一个长期以来修复的chrome错误引起的问题。原始答案如下。

chrome devtools doesn't even show the JSON as part of the request

这是真正的问题,这是chrome devtools的一个bug,在chrome 46中修复。

这段代码工作得很好——它正确地发布了JSON,只是看不到它。

I'd expect to see the object I've sent back

这不起作用,因为这不是jfiddle echo的正确格式。

正确的代码是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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有效负载的端点,原始代码是正确的。


我认为你的问题是jsfiddle只能处理form-urlencoded的请求。

但是,提出JSON请求的正确方法是将正确的json作为主体传递:

1
2
3
4
5
6
7
8
9
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));


在搜索引擎中,我以非JSON发布带有fetch的数据结束了这个主题,所以我想添加这个主题。

对于非JSON,您不必使用表单数据。您只需将Content-Type头设置为application/x-www-form-urlencoded并使用字符串:

1
2
3
4
5
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字符串的方法是使用库,而不是像我上面所做的那样将其打印出来。例如,来自query-stringqs包的stringify功能。因此,使用它看起来像:

1
2
3
4
5
6
import queryString from 'query-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: queryString.stringify({for:'bar', blah:1}
});


在花费了一些时间之后,逆向工程JSfiddle,尝试生成有效负载——这是一个效果。

请注意在线return response.json();上的响应不是响应-这是承诺。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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);
});

jfiddle:http://jsfiddle.net/egxt6cpz/46/&;firefox>39&;chrome>42


如果您使用的是纯JSON REST API,那么我已经创建了一个fetch()的瘦包装器,并进行了许多改进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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种方法:

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

async函数中:

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

jquery示例:

1
2
3
4
5
6
7
8
$('.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');
});


如果有同样的问题-没有从客户机向服务器发送body

添加Content-Type头为我解决了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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 => {
   ...
})


这与Content-Type有关。正如您可能从其他讨论和回答中注意到的,有些人可以通过设置Content-Type: 'application/json'来解决这个问题。不幸的是,在我的情况下,它不起作用,我的post请求在服务器端仍然是空的。

但是,如果您尝试使用jquery的$.post(),它可以工作,原因可能是jquery使用Content-Type: 'x-www-form-urlencoded',而不是application/json

1
2
3
4
5
6
7
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'}
})


如果您的JSON负载包含数组和嵌套对象,我将使用URLSearchParams和jquery的param()方法。

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

在您的服务器上,这看起来像一个标准的html

POSTed。


它可能对某些人有用:

我有一个问题,那就是formdata没有按我的要求发送。

在我的例子中,它是以下标题的组合,这些标题也导致了问题和错误的内容类型。

所以我在请求时发送这两个头文件,当我删除工作的头文件时,它没有发送formdata。

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

另外,正如其他答案所表明的,内容类型的标题需要是正确的。

对于我的请求,正确的内容类型头是:

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

所以底线是,如果表单数据没有附加到请求中,那么它可能是您的头。尝试将标题最小化,然后逐个添加,以查看是否解决了问题。


最上面的答案不适用于PHP7,因为它有错误的编码,但我可以用其他答案找出正确的编码。此代码还发送身份验证cookie,在处理诸如php论坛时,您可能需要这些cookie:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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);
    });
}

我认为,我们不需要将JSON对象解析成字符串,如果远程服务器接受JSON的请求,只需运行:

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

比如curl请求

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

如果远程服务器不接受JSON文件作为主体,只需发送一个数据表单:

1
2
3
4
5
6
7
8
9
10
11
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请求

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