Как я могу сделать Cloudflare рабочим, который перезаписывает код состояния ответа, но сохраняет остальную часть ответа?
В частности, меня интересует изменение всех ответов с кодом 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 ответ
Вы наткнулись на реальную проблему со спецификацией Fetch API, как она написана сегодня.
На данный момент, status
, statusText
, а также headers
являются единственными стандартными свойствами структуры инициализации Response. Однако нет гарантии, что они навсегда останутся единственными свойствами, и нет гарантии, что реализация не предоставляет дополнительных нестандартных или еще не стандартных свойств.
Фактически, Cloudflare Workers сегодня реализует нестандартное свойство: webSocket
, который используется для реализации проксирования WebSocket. Это свойство присутствует, если запрос передан fetch()
был запрос инициирования WebSocket, и сервер-источник завершил рукопожатие WebSocket. В этом случае, если вы уроните webSocket
поле из Response
, Проксирование WebSocket сломается - что может иметь или не иметь значение для вас.
К сожалению, стандарт не определяет какой-либо хороший способ переписать одно свойство Response
без потенциальной потери непредвиденных свойств. Это отличается от Request
объекты, которые предлагают (несколько неловкий) способ сделать такие переписывания: Request
конструктор может занять другое Request
Объект в качестве первого параметра, в этом случае второй параметр указывает только свойства для изменения. Альтернативно, чтобы изменить только URL, вы можете передать URL в качестве первого параметра и Request
объект как второй параметр. Это работает, потому что Request
объект оказывается той же "формы", что и структура инициализатора конструктора (неясно, намеревались ли это авторы спецификаций или это был счастливый случай). Exmaples:
// 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);
Но, конечно, это было ужасно.
Я предложил улучшения для API Fetch, чтобы решить эту проблему, но у меня еще не было времени их выполнить.:(