【问题标题】:How to get/set multiple 'Set-Cookie' Headers using Fetch API?如何使用 Fetch API 获取/设置多个“Set-Cookie”标头?
【发布时间】:2020-08-05 20:25:26
【问题描述】:

您可能知道,RFC 6265 表示允许有多个带有Set-Cookie 名称的标头。

但是,Fetch API 不允许这样做,因为其 Headers 接口公开的所有方法(包括 get()set()append()entries() 和所有其他方法)都已实现将所有具有相同名称的标头的值合并为一个以逗号分隔的标头。

例如,如果我们这样做:

var headers = new Headers();
headers.append('content-type', 'text/plain');
headers.append('set-cookie', 'test1=v; Max-Age=0');
headers.append('set-cookie', 'test2=v; Max-Age=0');
headers.append('set-cookie', 'test3=v; Max-Age=0');

然后我们尝试使用get('set-cookie') 读取set-cookie 值,或者通过使用entries() 迭代headers 变量,我们得到:

'set-cookie' : test1=v; Max-Age=0, test2=v; Max-Age=0, test3=v; Max-Age=0

请注意,如果我们尝试读取或操作具有多个同名标头的现有响应对象(即由可能支持此类允许行为的其他框架创建)的现有响应对象,也会发生相同的错误行为:换句话说,似乎像Fetch API 完全无法正确处理这种情况。

现在,虽然某些标头(例如 Accept)需要这种行为,但大多数浏览器(包括 Chrome 和 Firefox)都无法正确解析 Set-Cookie 标头,从而导致未正确设置 cookie。

这是一个已知的错误吗?如果是这种情况,是否有可用的解决方法来克服这个问题?

【问题讨论】:

  • 我相信这是唯一一个不允许像这样合并的 HTTP 标头。所以我对这个案子没有被考虑并不完全感到震惊。迭代标题时是否会获得单独的条目(使用.entries()
  • @Evert 不,正如我上面解释的那样,所有这些方法都将返回合并为一个的值,而不管响应对象实际包含什么。换句话说,您总是得到一个(合并的)Set-Cookie 条目。
  • Set-Cookie 由服务器发送,而不是客户端发送。为什么在 Fetch API 中需要它?
  • @Barmar 我开发了一个 CORS 代理,可以将请求转发到上游并响应客户端,因此我必须以各种方式处理 HTTP 响应。如果您有兴趣,请私信我,我会发送详细信息,但请相信我 - 我肯定需要这个答案。
  • 如果您想尝试获得赏金,here 是我需要修复/解决方法的代码(第 240 行及以下)。阅读 cmets 应该足以了解我遇到的问题以及我想获得什么。

标签: javascript cookies fetch fetch-api response-headers


【解决方案1】:

这是标准的一个已知“问题”。它实际上是Headers 部分中Fetch API standard 的第一个注释:

header 列表不同,Headers 对象不能代表多个Set-Cookie 标头。在某种程度上,这是有问题的,因为与所有其他标头不同,Set-Cookie 标头无法组合,但由于Set-Cookie 标头未暴露给客户端 JavaScript,因此这被认为是可接受的折衷方案。实现可以为标题列表选择更有效的 Headers 对象表示,只要它们还支持 Set-Cookie 标题的关联数据结构。

您可以阅读更多内容,甚至可以在spec's repo 中提出您自己的问题。
不过,已经有一些问题详细讨论了Set-Cookie 案例:

您提到了使用变通方法,但这实际上取决于您的用例。
注释提到使用二级结构来处理这些。
如果您真的想将这些 cookie 存储在 Headers 对象中,您可以添加自定义标头来存储它们:

new Headers([
  ['X-MyOwn-Set-Cookie-1', 'cookie1=value1'],
  ['X-MyOwn-Set-Cookie-2', 'cookie2=value2']
]);

显然,这不是标准可接受的解决方案,但也许您的实际考虑可能符合这种妥协。


正如本注释和 cmets 中的 @Barmar 所指出的,您通常使用来自服务器而不是前端的 Set-Cookie
比如设置多个Set-Cookieexpress是没有问题的:

test.js

const express = require('express');

const app = express();

const cookies = [
  { key: 'cookie1', value: 'value1' },
  { key: 'cookie2', value: 'value2' },
];

app.get('*', (req, res) => {
  console.log(req.url);
  for (const { key, value } of cookies) {
    res.cookie(key, value, { expires: new Date(Date.now() + 1000 * 60), httpOnly: true });
  }
  res.status(200).send('Success');
});

app.listen(3000, () => console.log(`Listening on http://localhost:3000/`));

1 号航站楼

$ node test.js
Listening on http://localhost:3000/

2 号航站楼

$ curl -v http://localhost:3000/
[...]
< HTTP/1.1 200 OK
< X-Powered-By: Express
< Set-Cookie: cookie1=value1; Path=/; Expires=Tue, 04 Aug 2020 19:45:53 GMT; HttpOnly
< Set-Cookie: cookie2=value2; Path=/; Expires=Tue, 04 Aug 2020 19:45:53 GMT; HttpOnly
< Content-Type: text/html; charset=utf-8
[...]

【讨论】:

  • 感谢您的解释,它确实为我已经从 Google 中挖掘出来的内容添加了一些内容。正如您通过阅读我给@Barman 的答案所看到的那样,我需要找到一个可以被最常见的浏览器(Chrome、Firefox、IE、Edge、Safari)接受的合适的解决方法,因为我正在开发“无服务器" CORS 代理(托管在 Cloudflare Worker 中),旨在将 HTTP 请求转发到上游,并将 HTTP 响应转发给客户端——因此需要“重建”一个包含多个 Set-Cookie 实例的有效标头列表,以便以适当的方式为客户端提供数据。
  • 如果您想尝试获得赏金,here 是我需要修复/解决方法的代码(第 240 行及以下)。阅读 cmets 应该足以了解我遇到的问题以及我想获得什么。
猜你喜欢
  • 2019-05-18
  • 1970-01-01
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 2019-11-12
  • 1970-01-01
  • 2019-01-22
  • 2018-09-16
相关资源
最近更新 更多