3

具体来说,我有兴趣将代码 403 的所有响应更改为代码 404,并将代码 301 的所有响应更改为 302。我不希望响应的任何其他部分发生更改,除了状态文本(我想为空) . 以下是我自己的尝试:

addEventListener("fetch", event => {
  event.respondWith(fetchAndModify(event.request));
});

async function fetchAndModify(request) {
  // Send the request on to the origin server.
  const response = await fetch(request);

  const body = await response.body
  newStatus = response.status
  if (response.status == 403) {
    newStatus = 404
  } else if (response.status == 301) {
    newStatus = 302
  }

  // Return modified response.
  return new Response(body, {
    status: newStatus,
    statusText: "",
    headers: response.headers
  });
}

我已确认此代码有效。我想知道这是否有可能覆盖除状态代码或文本之外的部分响应,如果是这样,我该如何避免这种情况?如果这违背了 Cloudflare worker 或 javascript 的某些最佳实践,请描述哪些以及为什么。

4

1 回答 1

6

您偶然发现了今天编写的 Fetch API 规范的一个真正问题。

到目前为止,statusstatusTextheaders是 Response 的 init 结构的唯一标准属性。但是,不能保证它们永远是唯一的属性,也不能保证实现不提供额外的非标准或尚不标准的属性。

事实上,今天的 Cloudflare Workers 实现了一个非标准属性:webSocket,用于实现 WebSocket 代理。如果传递给的请求fetch()是 WebSocket 启动请求并且源服务器完成了 WebSocket 握手,则存在此属性。在这种情况下,如果您webSocket从 中删除该字段Response,WebSocket 代理将中断——这对您来说可能很重要,也可能无关紧要。

不幸的是,该标准没有指定任何好的方法来重写 a 的单个属性,Response而不会潜在地丢弃意外的属性。这与Request对象不同,后者确实提供了一种(有些尴尬)的方式来进行此类重写:Request的构造函数可以将另一个Request对象作为第一个参数,在这种情况下,第二个参数仅指定要修改的属性。或者,要仅修改 URL,可以将 URL 作为第一个参数传递,将Request对象作为第二个参数传递。这是有效的,因为一个Request对象恰好与构造函数的初始化结构具有相同的“形状”(尚不清楚规范作者是否有意这样做,或者这是否是一个愉快的意外)。示例:

// change URL
request = new Request(newUrl, request);

// change method (or any other property)
request = new Request(request, {method: "GET"});

但是对于Response,您不能将现有Response对象作为第一个参数传递给Response的构造函数。有直接的方法来修改正文和标题:

// change response body
response = new Response(newBody, response);

// change response headers
// Making a copy of a Response object makes headers mutable.
response = new Response(response.body, response);
response.headers.set("Foo", "bar");

但是如果你想修改status......好吧,你可以做一个技巧,但它并不漂亮:

// Create an initializer by copying the Response's enumerable fields
// into a new object.
let init = {...response};

// Modify it.
init.status = 404;
init.statusText = "Not Found";

// Work around a bug where `webSocket` is `null` but needs to be `undefined`.
// (Sorry, I only just noticed this when testing this answer! We'll fix this
// in the future.)
init.webSocket = init.webSocket || undefined;

// Create a new Response.
response = new Response(response.body, init);

但是,呃,那肯定是丑陋的。

我已经提议改进 Fetch API来解决这个问题,但我还没有时间跟进它们。:(

于 2018-07-24T00:37:11.863 回答