我怎样才能让 Cloudflare 工作人员覆盖响应状态代码但保留其余响应?
How can I make a Cloudflare worker which overwrites a response status code but preserves the rest of the response?
具体来说,我有兴趣将代码 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 的某些最佳实践,请说明是哪些以及原因。
您偶然发现了今天编写的 Fetch API 规范的一个真正问题。
截至目前,status
、statusText
和 headers
是 Response 初始化结构的唯一标准属性。但是,不能保证它们永远是唯一的属性,也不能保证实现不提供额外的 non-standard 或 not-yet-standard 属性。
事实上,Cloudflare Workers 今天实现了一个 non-standard 属性: webSocket
,用于实现 WebSocket 代理。如果传递给 fetch()
的请求是 WebSocket 启动请求并且源服务器完成 WebSocket 握手,则此 属性 存在。在这种情况下,如果您从 Response
中删除 webSocket
字段,WebSocket 代理将中断——这对您来说可能重要也可能无关紧要。
不幸的是,该标准没有指定任何好的方法来重写 Response
的单个 属性 而不会丢失意外的属性。这不同于 Request
objects,后者确实提供了一种(有点笨拙的)方式来进行此类重写:Request
的构造函数可以采用另一个 Request
object作为第一个参数,在这种情况下,第二个参数仅指定要修改的属性。或者,要仅修改 URL,您可以将 URL 作为第一个参数传递,将 Request
object 作为第二个参数传递。这是可行的,因为 Request
object 恰好与构造函数的初始化结构相同 "shape" (尚不清楚规范作者是否有意这样做,或者这是一个意外的意外)。例子:
// change URL
request = new Request(newUrl, request);
// change method (or any other property)
request = new Request(request, {method: "GET"});
但对于 Response
,您不能将现有的 Response
object 作为第一个参数传递给 Response
的构造函数。有直接的方法来修改 body 和 headers:
// 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 来解决这个问题,但我还没有时间跟进它们。 :(
具体来说,我有兴趣将代码 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 的某些最佳实践,请说明是哪些以及原因。
您偶然发现了今天编写的 Fetch API 规范的一个真正问题。
截至目前,status
、statusText
和 headers
是 Response 初始化结构的唯一标准属性。但是,不能保证它们永远是唯一的属性,也不能保证实现不提供额外的 non-standard 或 not-yet-standard 属性。
事实上,Cloudflare Workers 今天实现了一个 non-standard 属性: webSocket
,用于实现 WebSocket 代理。如果传递给 fetch()
的请求是 WebSocket 启动请求并且源服务器完成 WebSocket 握手,则此 属性 存在。在这种情况下,如果您从 Response
中删除 webSocket
字段,WebSocket 代理将中断——这对您来说可能重要也可能无关紧要。
不幸的是,该标准没有指定任何好的方法来重写 Response
的单个 属性 而不会丢失意外的属性。这不同于 Request
objects,后者确实提供了一种(有点笨拙的)方式来进行此类重写:Request
的构造函数可以采用另一个 Request
object作为第一个参数,在这种情况下,第二个参数仅指定要修改的属性。或者,要仅修改 URL,您可以将 URL 作为第一个参数传递,将 Request
object 作为第二个参数传递。这是可行的,因为 Request
object 恰好与构造函数的初始化结构相同 "shape" (尚不清楚规范作者是否有意这样做,或者这是一个意外的意外)。例子:
// change URL
request = new Request(newUrl, request);
// change method (or any other property)
request = new Request(request, {method: "GET"});
但对于 Response
,您不能将现有的 Response
object 作为第一个参数传递给 Response
的构造函数。有直接的方法来修改 body 和 headers:
// 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 来解决这个问题,但我还没有时间跟进它们。 :(