【问题标题】:Reading response headers with Fetch API使用 Fetch API 读取响应标头
【发布时间】:2017-09-06 18:36:25
【问题描述】:

我正在使用具有"*://*/*" 权限的 Google Chrome 扩展程序,我正在尝试从 XMLHttpRequest 切换到 Fetch API

该扩展存储用户输入的登录数据,这些数据过去直接放入 XHR 的 open() 调用以进行 HTTP Auth,但在 Fetch 下不能再直接用作参数。对于 HTTP Basic Auth,绕过这个限制是微不足道的,因为您可以手动设置 Authorization 标头:

fetch(url, {
  headers: new Headers({ 'Authorization': 'Basic ' + btoa(login + ':' + pass) })
  } });

HTTP Digest Auth 然而需要更多的交互性;您需要读取服务器通过其 401 响应发送给您的参数以制作有效的授权令牌。我试过用这个 sn-p 读取WWW-Authenticate 响应头字段:

fetch(url).then(function(resp) {
  resp.headers.forEach(function(val, key) { console.log(key + ' -> ' + val); });
}

但我得到的只是这个输出:

content-type -> text/html; charset=iso-8859-1

这本身是正确的,但根据 Chrome 的开发者工具,这仍然缺少大约 6 个字段。如果我使用resp.headers.get("WWW-Authenticate")(或任何其他字段),我只会得到null

是否有机会使用 Fetch API 访问其他字段?

【问题讨论】:

    标签: javascript google-chrome-extension


    【解决方案1】:

    当您在 CORS 上使用 Fetch API 时,访问响应标头会受到限制。由于此限制,您只能访问以下标准标头:

    • Cache-Control
    • Content-Language
    • Content-Type
    • Expires
    • Last-Modified
    • Pragma

    当您为 Google Chrome 扩展程序编写代码时,您使用的是CORS,因此您无法访问所有标题。如果控制服务器,可以在响应中返回自定义信息body,而不是headers

    有关此限制的更多信息 - https://developers.google.com/web/updates/2015/03/introduction-to-fetch#response_types

    【讨论】:

    • @jules CORS 的这个限制尊重access-control-expose-headers 中的值——或者可能是access-control-allow-headers(我们把它放在两者中)。
    • access-control-expose-headers 为我工作了从服务器返回的标头 - 然后标头可通过获取响应 Headers 对象获得。并且access-control-allow-headers 用于允许服务器上的请求标头(或者我会从服务器收到错误消息)
    • 这有点愚蠢,这在 Fetch 中是不可能的,但可以通过 XmlHttpRequest 来完成。如果通过变通方法仍然可行,那么安全优势是什么?
    • @sebas 看起来 Chrome(可能还有其他浏览器)对 XmlHttpRequest 施加了相同的限制。
    • 设置Access-Control-Allow-Headers,当允许头部从客户端传递到服务器时(例如If-Match)。当允许标头从服务器传回客户端时设置Access-Control-Expose-Headers(例如ETag)。
    【解决方案2】:

    如果不是 CORS:

    Fetch 在调试或console.log 响应时不显示标题。

    您必须使用以下方式访问标题。

    response.headers.get('x-auth-token')
    

    【讨论】:

    • response.headers.map 在这里显示 react native 中的所有标头不确定。
    【解决方案3】:

    来自MDN

    您还可以通过访问条目迭代器来获取所有标题。

    // Display the key/value pairs
    for (var pair of res.headers.entries()) {
       console.log(pair[0]+ ': '+ pair[1]);
    }
    

    另外,请记住this 部分:

    出于安全原因,某些标头只能由用户代理控制。这些标头包括禁止标头名称和禁止响应标头名称。

    【讨论】:

    • 使用迭代器呈现相同的输出;只有内容类型字段。并且禁止的标题名称列表似乎只适用于修改,WWW-Authenticate 也没有列出。
    【解决方案4】:

    为了向后兼容不支持 ES2015 迭代器(并且可能还需要 fetch/Promise polyfills)的浏览器,Headers.forEach 函数是最佳选择:

    r.headers.forEach(function(value, name) {
        console.log(name + ": " + value);
    });
    

    在 IE11 中测试,Bluebird 作为 Promise polyfill,whatwg-fetch 作为 fetch polyfill。 Headers.entries()、Headers.keys() 和 Headers.values() 不起作用。

    【讨论】:

      【解决方案5】:

      问题:

      您可能认为这是前端问题。
      这是一个后端问题。
      浏览器不允许公开 Authorization 标头,除非 Backend 告诉浏览器明确公开它。

      如何解决:

      这对我有用。
      在后端(API)中,将其添加到响应头中:

      response.headers.add("Access-Control-Expose-Headers","Authorization")
      

      为什么?

      安全性。
      防止 XSS 攻击。
      这个请求应该是从后端到后端的。
      并且后端会将 httpOnly cookie 设置到前端。
      因此,您网站上的任何第三方 JS 包都不应访问授权标头。
      如果您认为让前端可访问标头是安全的,请执行此操作。
      但我建议立即将服务器后端设置的 HttpOnly Cookies 发送到您的浏览器。

      参考:

      https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Expose-Headers

      【讨论】:

        【解决方案6】:

        对于我们解决这个限制问题,添加公开的标头名称就足够了。

        access-control-expose-headers: headername1, headername2, ...

        设置此标头后,客户端脚本能够从响应中读取这些标头(headername1、headername2、...)。

        【讨论】:

          【解决方案7】:

          还请记住,如果您解析它,您可能需要在res.headers.get() 之后将响应传递给下一个.then()。我总是忘记这件事......

          var link
          const loop = () => {
            fetch(options)
              .then(res => { 
                link = res.headers.get('link')
                return res.json()
              })
              .then(body => {
                for (let e of body.stuff) console.log(e)
                if (link) setTimeout(loop, 100)
              })
              .catch(e => {
                throw Error(e)
              })
          }
          loop()
          

          【讨论】:

            猜你喜欢
            • 2016-09-02
            • 2014-06-15
            • 2018-06-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-09-24
            • 2017-12-05
            • 2014-05-24
            相关资源
            最近更新 更多