关于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作为请求的一部分显示,这意味着它不会被发送。

  • 您使用的浏览器是什么?
  • @Krzysztoffsafjanowski Chrome42,这意味着要有全面的获取支持
  • 检查这个fiddle jsfiddle.net/abpbah4/2您期望什么数据?因为fiddle.jshell.net/echo/json的get请求显示空对象。{}
  • @Kaushikkishore编辑以澄清预期产出。res.json()应返回{a: 1, b: 2}
  • @Razor我用REST客户端的后数据{"a":1,"b":2}content-type: application/json称这个https://fiddle.jshell.net/echo/json,我得到了{}对象,表明fetch没有问题。我是这么认为的
  • @razor jsfiddle.net/abbbah4/3在这个小提琴中,我添加了一个调试器。当调试器点击时,检查EDOCX1的值〔8〕,这次您的promise对象不在那里。因此,该方法是成功的。那可能有效。;)
  • 您忘记包含包含要发送的数据的json属性。然而,我的body无论如何都没有得到正确的治疗。看看这个小提琴,看看5秒的延迟被跳过。jsfiddle.net/99arsnkg同样,当您试图添加额外的头时,它们会被忽略。这可能是fetch()本身的问题。


有了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有效负载的端点,原始代码是正确的。

  • 对于记录而言,这不是发布JSON有效负载——这是一个表单发布(x-www-form-urlencoded,在名为json的字段中包含一个JSON数据。所以数据是双重编码的。有关干净的JSON帖子,请参见下面@vp arth的回答。
  • @这不是一个x-www-form-urlen编码的帖子。FETCH API始终对FormData对象使用多部分/表单数据编码。
  • @Jukkap I站正确。我的主要观点是双重编码问题。
  • 内容类型仍然是text/html;charset=iso-8859-1不知道我做错了什么…
  • 为了安全起见,最好确认res.ok,以防响应代码出现某种错误。最后还有一个.catch()条款也不错。我知道这只是一个示例片段,但要记住这些内容,以便在现实世界中使用。


我认为你的问题是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));

  • TNXASP.NET MVC中的正确解决方案。
  • 这是正确的解决方案,句号-其他人似乎都混淆了x-www-form-urlencodedapplication/json,要么是不匹配,要么是在URL编码的字符串中双重包装json。
  • 但这对jsiddle不起作用。所以,我不确定为什么你会说"这是正确的解决方案,句号"。其他人不是都在做包装以满足jfiddle的/echo路由的API吗?


在搜索引擎中,我以非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}
});

  • 非常感谢您提供的查询字符串,我用json.stringify尝试了很多次,但Ajax没有返回响应。但是查询字符串做到了。我还发现这是因为fetch为body参数创建json,而不是创建一个字符串。
  • 谢谢你!这是最好的回答!我昨天撞到墙上了几个小时,试图找到一种方法,将"body"和表单数据从我的Web应用程序发送到我的服务器…一个建议:$npm install cors--save这是为了在fetch请求中去掉"mode:'no cors'",请参见github.com/expressjs/cors。
  • 谢谢@alexandercherednichenko!感谢你分享我不知道的一个有趣的CORS纸条。:)
  • 非常感谢,我完全错过了正确的Content-Type,我的PHP后端没有收到任何东西。'application/x-www-form-urlencoded'把它修好了。


在花费了一些时间之后,逆向工程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

  • 为什么用'x-www-form-urlencoded代替application/json?有什么区别?
  • @JuanPicado——在两年前的jsiddle逆向工程之后,它只是一个可行的选择。当然,application/json是正确的形式,它现在起作用了。谢谢你的好眼力:)
  • YW。奇怪的是,它以老的方式与EDOCX1(stackoverflow.com/questions/41984893/…)而不是application/json一起工作。也许你知道为什么…
  • Content-Typeapplication/json,但你的实际body似乎是x-www-form-urlencoded——我认为这不应该奏效?如果它可以工作,您的服务器必须相当宽容。下面@vp ou arth给出的答案似乎是正确的。


如果您使用的是纯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');
});

  • 我认为你对Object.assign是指一组不同的论点?应该是Object.assign({}, api.headers, headers),因为您不想一直在普通api.headers的哈希中添加自定义headers。正确的?
  • @Mobigital完全正确,我当时不知道那细微差别,但现在这是我唯一的方法。


如果有同样的问题-没有从客户机向服务器发送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'}
})

  • 我的后端开发人员用PHP构建了这个API,希望数据像查询字符串一样,而不是JSON对象。这解决了服务器端的空响应。


如果您的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'

  • 这显然是错误的。它与服务器端无关,无论您是否需要将JSON串接起来。这正是您的curl命令隐式执行的操作!如果在将对象作为body传递之前不将它们串起来,您只需发送"[object Object]"作为请求的主体。开发工具中的一个简单测试将向您展示这一点。打开它,在不离开此选项卡的情况下尝试:a = new FormData(); a.append("foo","bar"); fetch("/foo/bar", { method: 'POST', body: {}, headers: { 'Content-type': 'application/json' } })