我如何才能使Cloudflare工作者覆盖响应状态代码,但保留其余响应?

时间:2018-07-23 19:14:41

标签: javascript cloudflare cloudflare-workers

我特别想将代码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工作者或javascript的某些最佳做法,请说明哪些最佳做法以及原因。

1 个答案:

答案 0 :(得分:3)

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

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

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

不幸的是,该标准未指定任何好的方法来重写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);

但是,嗯,那肯定很丑。

我有proposed improvements to the Fetch API可以解决这个问题,但是我还没有时间来解决这些问题。 :(