简体   繁体   English

使用 Fetch API 读取响应标头

[英]Reading response headers with Fetch API

I'm in a Google Chrome extension with permissions for "*://*/*" and I'm trying to make the switch from XMLHttpRequest to the Fetch API .我在具有"*://*/*"权限的 Google Chrome 扩展程序中,我正在尝试从 XMLHttpRequest 切换到Fetch API

The extension stores user-input login data that used to be put directly into the XHR's open() call for HTTP Auth, but under Fetch can no longer be used directly as a parameter.该扩展存储了用户输入的登录数据,这些数据过去直接放入 XHR 的 open() 调用以进行 HTTP Auth,但在 Fetch 下不能再直接用作参数。 For HTTP Basic Auth, circumventing this limitation is trivial, as you can manually set an Authorization header:对于 HTTP Basic Auth,规避此限制是微不足道的,因为您可以手动设置 Authorization 标头:

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

HTTP Digest Auth however requires more interactivity;然而, HTTP Digest Auth需要更多的交互性; you need to read parameters that the server sends you with its 401 response to craft a valid authorization token.您需要读取服务器通过其 401 响应发送给您的参数以制作有效的授权令牌。 I've tried reading the WWW-Authenticate response header field with this snippet:我试过用这个片段阅读WWW-Authenticate响应头字段:

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

But all I get is this output:但我得到的只是这个输出:

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

Which by itself is correct, but that's still missing around 6 more fields according to Chrome's Developer Tools.这本身是正确的,但根据 Chrome 的开发者工具,这仍然缺少大约 6 个字段。 If I use resp.headers.get("WWW-Authenticate") (or any of the other fields for that matter), i get only null .如果我使用resp.headers.get("WWW-Authenticate") (或任何其他字段),我只会得到null

Any chance of getting to those other fields using the Fetch API?是否有机会使用 Fetch API 访问其他字段?

There is a restriction to access response headers when you are using Fetch API over CORS.当您在 CORS 上使用 Fetch API 时,访问响应标头存在限制。 Due to this restriction, you can access only following standard headers:由于此限制,您只能访问以下标准标头:

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

When you are writing code for Google Chrome extension, you are using CORS , hence you can't access all headers.当您为 Google Chrome 扩展程序编写代码时,您使用的是CORS ,因此您无法访问所有标头。 If you control the server, you can return custom information in the response body instead of headers如果您控制服务器,则可以在响应body而不是headers中返回自定义信息

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

If it's NOT CORS:如果不是 CORS:

Fetch does not show headers while debugging or if you console.log response. Fetch 不会在调试时或console.log响应时显示标题

You have to use following way to access headers.您必须使用以下方式访问标题。

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

From MDN来自MDN

You can also get all the headers by accessing the entries Iterator.您还可以通过访问条目迭代器来获取所有标题。

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

Also, keep in mind this part:另外,请记住部分:

For security reasons, some headers can only be controlled by the user agent.出于安全原因,某些标头只能由用户代理控制。 These headers include the forbidden header names and forbidden response header names.这些标头包括禁止标头名称和禁止响应标头名称。

The Problem:问题:

You may think this is a Frontend Problem.你可能认为这是一个前端问题。
It is a backend problem.这是一个后端问题。
The browser will not allow to expose the Authorization header, unless if the Backend told the browser to expose it explicitly.浏览器不允许公开Authorization标头,除非后端告诉浏览器显式公开它。

How To Solve It:如何解决:

This worked for me.这对我有用。
In the backend (The API), add this to the response header:在后端(API)中,将其添加到响应标头中:

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

Why?为什么?

Security.安全。
To prevent XSS exploits.防止 XSS 攻击。
This request is supposed to be from backend to backend.这个请求应该是从后端到后端的。
And the backend will set up the httpOnly cookie to the frontend.后端将设置 httpOnly cookie 到前端。
So the authorization header should not be accessible by any third party JS package on your website.因此,您网站上的任何第三方 JS 包都不应访问授权标头。
If you think that it safe is to make the header accessible by frontend, do it.如果您认为让前端可以访问标头是安全的,那就去做吧。
But I recommend HttpOnly Cookies set up by the server backend to your browser immediately.但我建议立即将服务器后端设置的 HttpOnly Cookies 发送到您的浏览器。

Reference:参考:

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

For backward compatibility with browsers that do not support ES2015 iterators (and probably also need fetch/Promise polyfills), the Headers.forEach function is the best option:为了向后兼容不支持 ES2015 迭代器(并且可能还需要 fetch/Promise polyfills)的浏览器, Headers.forEach函数是最佳选择:

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

Tested in IE11 with Bluebird as Promise polyfill and whatwg-fetch as fetch polyfill.在 IE11 中测试,Bluebird 作为 Promise polyfill,whatwg-fetch 作为 fetch polyfill。 Headers.entries(), Headers.keys() and Headers.values() does not work. Headers.entries()、Headers.keys() 和 Headers.values() 不起作用。

For us to fix this restriction issue, adding exposed header names is good enough.对于我们解决这个限制问题,添加公开的标头名称就足够了。

access-control-expose-headers: headername1, headername2, ...访问控制公开标头:标头名称1,标头名称2,...

After setting this header, the client side script is able to read those headers (headername1, headername2, ...) from the response.设置此标头后,客户端脚本能够从响应中读取这些标头(headername1,headername2,...)。

In response to a cross-origin request, add 'Access-Control-Expose-Headers': '*' to your response header, so that all headers are available to be read in your client side code.为了响应跨域请求,将'Access-Control-Expose-Headers': '*'添加到响应标头中,以便可以在客户端代码中读取所有标头。 You can also indicate which headers you want to expose by specifying the header names instead of a wildcard.您还可以通过指定标头名称而不是通配符来指示要公开的标头。

Note that per MDN the '*' wildcard is treated as a literal if the URL you are accessing has "credentials".请注意,如果您访问的 URL 具有“凭据”,则每个 MDN都会将“*”通配符视为文字。

Also remember you may need to pass the response to next .then() after res.headers.get() if you parse it.另请记住,如果您解析它,您可能需要在res.headers.get() .then()之后将响应传递给 next .then()。 I keep forgetting about that...我一直忘记这件事...

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()

If you're using .net in Program.cs file or Startup.cs depending on the .net version you have to do something like this:如果您在 Program.cs 文件或 Startup.cs 中使用 .net,具体取决于 .net 版本,您必须执行以下操作:

builder.Services.AddCors(options =>
{
    options.AddPolicy(MyAllowSpecificOrigins,
    builder =>
    {
        builder
            .SetIsOriginAllowed(p => true)
            .WithOrigins("http://localhost:3000", "http://*:3000")
            .AllowCredentials()
            .WithExposedHeaders("X-Pagination") /*custom header*/
            .AllowAnyHeader()
            .AllowAnyMethod();
    });
});

} }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM