提取:POST json数据


559

我正在尝试使用fetch发布 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}),但这不会发生-chrome devtools甚至不将JSON显示为请求的一部分,这意味着它没有被发送。


你使用的是什么浏览器?
Krzysztof Safjanowski

@KrzysztofSafjanowski chrome 42,它具有完整的获取支持
Razor

检查这个小提琴jsfiddle.net/abbpbah4/2 您期望什么数据?因为获取fiddle.jshell.net/echo/json的请求显示空对象。{}
Kaushik 2015年

@KaushikKishore编辑以澄清预期的输出。res.json()应该回来{a: 1, b: 2}
Razor

1
您忘记了包含json包含要发送的数据的属性。但是,body无论如何我都没有得到正确的对待。请看这个小提琴,以免跳过5秒的延迟。 jsfiddle.net/99arsnkg另外,当您尝试添加其他标题时,它们将被忽略。这可能是一个问题fetch()
boombox

Answers:


597

借助ES2017 async/await支持,这是如何实现POSTJSON负载的方法:

(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 bug引起的
原始答案如下。

chrome devtools甚至没有在请求中显示JSON

这是真正的问题这是 Chrome 46中修复的chrome devtools错误

该代码可以正常工作-它正确地发布了JSON,只是看不到。

我希望看到我寄回的物品

那是行不通的,因为那不是JSfiddle的echo正确格式

正确的代码是:

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帖子。Fetch API始终在FormData对象上使用multipart / form-data编码。
JukkaP

@ JukkaP我站纠正。我的主要观点是双重编码问题。
mindplay.dk '18

2
Content-Type仍然是text / html;charset = iso-8859-1不知道我在做什么错...
KT Works

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。因此,我不确定我是否理解您为什么会说“这是正确的解决方案,期间”。难道不是每个人都在做包装来满足jsfiddle /echo路线的API 吗?
亚当·贝克

69

在搜索引擎中,我最终遇到了有关使用fetch进行非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-stringqs打包的函数。因此,使用它看起来像:

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没有返回响应。但是查询字符串可以解决问题。我还发现这是因为fetch为body参数创建json而不是创建字符串。
丹麦

1
谢谢你,兄弟!这是最好的答复!昨天我在墙上碰了好几个小时,试图找到一种方法,将带有表单数据的“正文”从Web应用程序发送到服务器。...一个建议:$ npm install cors --save这是摆脱“模式:“ no-cors”“在获取请求中请参见github.com/expressjs/cors
Alexander Cherednichenko

谢谢@AlexanderCherednichenko!并感谢您分享这些核心提示,这是我不知道的有趣话题。:)
Noitidart '17

1
衷心的感谢。您节省了我的时间,也节省了我的生命两次:)
bafsar

1
谢谢@bafsar!
Noitidart '19

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-在2年前进行jsfiddle逆向工程之后,它只是可行的一种选择。当然application/json是正确的形式,并且现在可以使用。感谢您的关注
:)

嗯 好奇的细节,它对我有用fetchstackoverflow.com/questions/41984893/…)代替application/json。也许您知道为什么...
Juan Picado '02

6
Content-Typeapplication/json,但你的实际body似乎是x-www-form-urlencoded-我不认为这应该工作?如果它能正常工作,那么您的服务器一定很宽容。以下@vp_arth的答案似乎是正确的。
mindplay.dk

18

如果您使用的是纯JSON REST API,则我围绕fetch()创建了一个薄包装,并进行了许多改进:

// 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)因为您不想继续将custom添加headers到common的哈希中api.headers。对?
Mobigital '18

@Mobigital完全正确,那时候我还不知道这种细微差别,但现在这是我做到这一点的唯一方法
Francisco Presencia

11

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

但是,如果您尝试使用jQuery $.post()且它可以正常工作,则原因可能是因为jQuery使用了Content-Type: 'x-www-form-urlencoded'代替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
我的后端开发人员使用PHP构建了API,当时期望数据像查询字符串一样,而不是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

这对某人可能有用:

我遇到的问题是没有按我的要求发送formdata

在我的情况下,是以下标头的组合也导致了问题和错误的Content-Type。

因此,我随请求发送了这两个标头,而当我删除了有效的标头时,它没有发送formdata。

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

还有其他答案表明Content-Type标头需要正确。

对于我的请求,正确的Content-Type标头是:

“内容类型”:“应用程序/ x-www-form-urlencoded; charset = UTF-8”

因此,最重要的是,如果您的formdata没有附加到Request上,那么它可能是您的标头。尝试将标题最小化,然后尝试将它们一个接一个地添加,以查看问题是否得到解决。


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文件作为正文,则只需发送一个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
});

如卷曲要求

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

2
这显然是不正确的。是否需要将json字符串化与服务器端无关。这正是您的curl命令隐式执行的操作!如果不将它们作为前字符串化的对象body,你将只发送"[object Object]"作为请求的主体。开发工具中的一个简单测试将向您显示。打开它,然后尝试完成此操作,而无需离开此标签: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版。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.