【问题标题】:extract both JSON and headers from fetch()从 fetch() 中提取 JSON 和标头
【发布时间】:2017-06-08 07:11:33
【问题描述】:

我正在为一个简单的 react/redux 应用程序建模身份验证层。在服务器端,我有一个基于 devise_token_auth gem 的 API。

我正在使用fetch 发布登录请求:

const JSON_HEADERS = new Headers({
  'Content-Type': 'application/json'
});

export const postLogin = ({ email, password }) => fetch(
  `${API_ROOT}/v1/auth/sign_in`, {
    method: 'POST',
    headers: JSON_HEADERS,
    body: JSON.stringify({ email, password })
});

// postLogin({ email: 'test@test.it', password: 'whatever' });

这可行,我得到 200 响应和我需要的所有数据。我的问题是,信息在响应正文和标头之间划分。

  • 正文:用户信息
  • 标头:访问令牌、过期等

我可以这样解析 JSON 正文:

postLogin({ 'test@test.it', password: 'whatever' })
  .then(res => res.json())
  .then(resJson => dispatch(myAction(resJson))

但是myAction 不会从标头中获取任何数据(在解析 JSON 时丢失)。

有没有办法从fetch 请求中同时获取标头和正文? 谢谢!

【问题讨论】:

    标签: javascript response fetch-api response-headers


    【解决方案1】:

    我想我会分享一下我们最终解决这个问题的方法:只需在.then 链中添加一个步骤(在解析 JSON 之前)来解析 auth 标头并调度正确的操作:

    fetch('/some/url')
      .then(res => {
        const authHeaders = ['access-token', 'client', 'uid']
          .reduce((result, key) => {
            let val = res.headers.get(key);
            if (val) {
              result[key] = val;
            }
          }, {});
        store.dispatch(doSomethingWith(authHeaders)); // or localStorage
        return res;
      })
      .then(res => res.json())
      .then(jsonResponse => doSomethingElseWith(jsonResponse))
    

    另一种方法,灵感来自强大的 Dan Abramov (http://stackoverflow.com/a/37099629/1463770)

    fetch('/some/url')
      .then(res => res.json().then(json => ({
        headers: res.headers,
        status: res.status,
        json
      }))
    .then({ headers, status, json } => goCrazyWith(headers, status, json));
    

    HTH

    【讨论】:

    • 第二个似乎更适合一般用途。
    • 确实这就是我们最终在代码库中使用的内容。
    【解决方案2】:

    使用异步/等待:

    const res = await fetch('/url')
    const json = await res.json()
    doSomething(headers, json)
    

    没有异步/等待:

    fetch('/url')
      .then( res => {
        const headers = res.headers.raw())
        return new Promise((resolve, reject) => {
          res.json().then( json => resolve({headers, json}) )
        })
      })
      .then( ({headers, json}) => doSomething(headers, json) )
    

    Promise 的这种方法更通用。它在所有情况下都有效,即使创建捕获 res 变量的闭包不方便(如此处的另一个答案)。例如,当处理程序更复杂并被提取(重构)为分离的函数时。

    【讨论】:

    • 您好,感谢您的回答。不幸的是,myAction 需要将 promise 作为参数,因为 res.json() 返回一个。我们希望将纯数据传递给非 thunk 动作创建者。
    • 有人给我的答案加了星标并引起了我的注意,所以我添加了一种现代的 async / await 方法并修复了以前的答案。希望对读者有所帮助。
    【解决方案3】:

    我的 WP json API 解决方案

    fetch(getWPContent(searchTerm, page))
      .then(response => response.json().then(json => ({
        totalPages: response.headers.get("x-wp-totalpages"),
        totalHits: response.headers.get("x-wp-total"),
        json
      })))
      .then(result => {
        console.log(result)
      })
    

    【讨论】:

      【解决方案4】:

      如果您想将所有标头解析为一个对象(而不是保留迭代器),您可以执行以下操作(基于上述 Dan Abramov 的方法):

      fetch('https://jsonplaceholder.typicode.com/users')
          .then(res => (res.headers.get('content-type').includes('json') ? res.json() : res.text())
          .then(data => ({
              headers: [...res.headers].reduce((acc, header) => {
                  return {...acc, [header[0]]: header[1]};
              }, {}),
              status: res.status,
              data: data,
          }))
          .then((headers, status, data) => console.log(headers, status, data)));
      

      或在async 上下文/函数中:

      let response = await fetch('https://jsonplaceholder.typicode.com/users');
      
      const data = await (
          response.headers.get('content-type').includes('json')
          ? response.json()
          : response.text()
      );
      
      response = {
          headers: [...response.headers].reduce((acc, header) => {
              return {...acc, [header[0]]: header[1]};
          }, {}),
          status: response.status,
          data: data,
      };
      

      将导致:

      {
          data: [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}],
          headers: {
              cache-control: "public, max-age=14400"
              content-type: "application/json; charset=utf-8"
              expires: "Sun, 23 Jun 2019 22:50:21 GMT"
              pragma: "no-cache"
          },
          status: 200
      }
      

      根据您的用例,这可能更方便使用。此解决方案还考虑了在响应中调用 .json().text() 的内容类型。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-21
        • 2020-04-14
        • 2016-07-09
        • 1970-01-01
        • 1970-01-01
        • 2017-04-30
        • 2013-02-11
        • 2012-09-04
        相关资源
        最近更新 更多