获取:POST JSON数据

1058

我正在尝试使用fetch来POST一个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 echo 时,我期望能收到我发送的对象({a: 1, b: 2})作为返回,但事实并非如此 - Chrome 开发者工具甚至没有显示该 JSON 作为请求的一部分,这意味着它没有被发送。

@KrzysztofSafjanowski Chrome 42旨在具有完整的fetch支持(http://caniuse.com/#search=fetch) - Razor
检查这个代码片段 https://jsfiddle.net/abbpbah4/2/,你期望的数据是什么?因为对 https://fiddle.jshell.net/echo/json 的 GET 请求显示为空对象 {} - Kaushik
@KaushikKishore 编辑以澄清预期输出。res.json() 应返回 {a: 1, b: 2} - Razor
@Razor 在这个 jsfiddle 中,我添加了一个调试器。当调试器触发时,请检查 res 的值,此时您的 Promise 对象不在那里。因此,这个 fetch 可能会有成功方法。这可能有效。 ;) - Kaushik
1
你忘记包含包含要发送的数据的 json 属性了。然而,body 也没有被正确处理。请查看此 fiddle,以查看 5 秒延迟被跳过的情况:http://jsfiddle.net/99arsnkg/ 此外,当您尝试添加其他标头时,它们会被忽略。这可能是 fetch() 本身的问题。 - boombox
显示剩余3条评论
18个回答

1184

使用 ES2017 的 async/await 支持,以下是如何传输 JSON 负载进行 POST

(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的使用Promise的解决方案

然而,问题是由于一个早已修复的Chrome bug引起的。
以下是原始答案。

Chrome开发工具甚至不会显示JSON作为请求的一部分

这才是真正的问题所在,这是一个Chrome开发工具的bug,在Chrome 46中已经修复。

那段代码运行良好 - 它正确地POST了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负载的端点,原始代码是正确的

6
为了保险起见,最好在响应代码出现某种错误时确认 res.ok。最好在末尾添加一个 .catch() 子句。我意识到这只是示例代码,但在实际使用中请记住这些事情。 - Ken Lyon
也许我有些重复,但我想指出它同样可以成功地处理 PUT 请求。 - Diego Fortes
发送JSON数据时,我不需要'Accept': 'application/json'content-type - Timo
2
@Timo:这在很大程度上取决于你的服务器;服务器不是必须使用这些字段。但是,服务器可能会发出不同的数据,具体取决于Accept,并且它可能需要content-type接受json。最好将两者都设置好,以避免当服务器软件更新时进行无休止的调试。 - Guntram Blohm

353

我认为你遇到的问题是jsfiddle只能处理form-urlencoded请求。但正确的方法是将正确的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));


2
对于那些不熟悉箭头函数的人,你必须在那里使用 return res.json(),以便在下一个 .then() 调用中获取数据。 - Andrew
如果您不使用花括号,那么您不必使用return。 - Allan Taylor

111

通过搜索引擎,我进入了有关使用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 字符串的另一种替代方法,而不是像我上面那样打出来,是使用库。例如,从query-stringqs包中使用stringify函数。因此,使用它看起来像:

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

56

在花费一些时间反向工程 jsFiddle 并尝试生成有效载荷后,产生了一个效果。

请注意行 return response.json();,其中的 response 不是 response - 它是 promise。

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


有趣的细节是,我用旧的方式fetch(http://stackoverflow.com/questions/41984893/fetch-post-fails-on-post-json-body-with-sails-js?noredirect=1#comment71171442_41984893)而不是`application/json`,它对我起作用。也许你知道为什么... - Juan Picado
8
“Content-Type” 是 “application/json”,但是你实际的“body”似乎是 “x-www-form-urlencoded”-- 我认为这应该不会工作?如果确实可以工作,那么你的服务器一定非常宽容。下面 @vp_arth 给出的答案似乎是正确的。 - mindplay.dk
浪费了30分钟的时间,试图将一个等待响应转换为JSON。 - aex

42

2021的回答:如果您在寻找如何使用async/await或promises与axios相比制作GET和POST Fetch api请求,那么可以参考以下内容。

我将使用jsonplaceholder假API进行演示:

使用async/await的Fetch api GET请求:

         const asyncGetCall = async () => {
            try {
                const response = await fetch('https://jsonplaceholder.typicode.com/posts');
                 const data = await response.json();
                // enter you logic when the fetch is successful
                 console.log(data);
               } catch(error) {
            // enter your logic for when there is an error (ex. error toast)
                  console.log(error)
                 } 
            }


          asyncGetCall()

使用async/await的Fetch api POST请求:

    const asyncPostCall = async () => {
            try {
                const response = await fetch('https://jsonplaceholder.typicode.com/posts', {
                 method: 'POST',
                 headers: {
                   'Content-Type': 'application/json'
                   },
                   body: JSON.stringify({
             // your expected POST request payload goes here
                     title: "My post title",
                     body: "My post content."
                    })
                 });
                 const data = await response.json();
              // enter you logic when the fetch is successful
                 console.log(data);
               } catch(error) {
             // enter your logic for when there is an error (ex. error toast)

                  console.log(error)
                 } 
            }

asyncPostCall()

使用 Promises 发送 GET 请求:

  fetch('https://jsonplaceholder.typicode.com/posts')
  .then(res => res.json())
  .then(data => {
   // enter you logic when the fetch is successful
    console.log(data)
  })
  .catch(error => {
    // enter your logic for when there is an error (ex. error toast)
   console.log(error)
  })

使用 Promises 发送 POST 请求:

fetch('https://jsonplaceholder.typicode.com/posts', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
  },
   body: JSON.stringify({
     // your expected POST request payload goes here
      title: "My post title",
      body: "My post content."
      })
})
  .then(res => res.json())
  .then(data => {
   // enter you logic when the fetch is successful
    console.log(data)
  })
  .catch(error => {
  // enter your logic for when there is an error (ex. error toast)
   console.log(error)
  })  

Axios使用GET请求:

        const axiosGetCall = async () => {
            try {
              const { data } = await axios.get('https://jsonplaceholder.typicode.com/posts')
    // enter you logic when the fetch is successful
              console.log(`data: `, data)
           
            } catch (error) {
    // enter your logic for when there is an error (ex. error toast)
              console.log(`error: `, error)
            }
          }
    
    axiosGetCall()

Axios使用POST请求:

const axiosPostCall = async () => {
    try {
      const { data } = await axios.post('https://jsonplaceholder.typicode.com/posts',  {
      // your expected POST request payload goes here
      title: "My post title",
      body: "My post content."
      })
   // enter you logic when the fetch is successful
      console.log(`data: `, data)
   
    } catch (error) {
  // enter your logic for when there is an error (ex. error toast)
      console.log(`error: `, error)
    }
  }


axiosPostCall()

20

我已经创建了一个轻量级的包装器,可以对纯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');
});

19

我遇到了同样的问题——客户端没有向服务器发送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 => {
   ...
})

13

这与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'}
})

9

顶部的答案对于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);
    });
}

1
对于PHP,您还可以使用以下代码来获取数组: $data = json_decode(file_get_contents('php://input'), true); 该代码用于从以下请求中获取数组: fetch(requestURI, {method:'POST', body: JSON.stringify(object)}); - xgarb

7
**//POST a request**


const createTodo = async (todo) =>  {
    let options = {
        method: "POST",
        headers: {
            "Content-Type":"application/json",
        },
        body: JSON.stringify(todo)      
    }
    let p = await fetch("https://jsonplaceholder.typicode.com/posts", options);
    let response = await p.json();
    return response;
}

**//GET request**

const getTodo = async (id) => {
    let response = await fetch('https://jsonplaceholder.typicode.com/posts/' + id);
  let r = await response.json();
  return r;
}
const mainFunc = async () => {
    let todo = {
            title: "milan7",
            body: "dai7",
            userID: 101
        }
    let todor = await createTodo(todo);
    console.log(todor);
    console.log(await getTodo(5));
}
mainFunc()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接