Как скопировать объект запроса с другим URL-адресом?

Я пишу обертку вокруг fetch, которую я хотел бы добавить что-то в URL, прежде чем делать запрос, например. определение параметров запроса. Я не могу понять, как сделать копию данного объекта Request с другим URL-адресом, чем оригинал. Мой код выглядит так:

// My function which tries to modify the URL of the request
function addLangParameter(request) {
    const newUrl = request.url + "?lang=" + lang;
    return new Request(newUrl, /* not sure what to put here */);
}

// My fetch wrapper
function myFetch(input, init) {
    // Normalize the input into a Request object
    return Promise.resolve(new Request(input, init))
        // Call my modifier function
        .then(addLangParameter)
        // Make the actual request
        .then(request => fetch(request));
}

Я попытался поместить исходный запрос в качестве второго аргумента в конструктор Request, например:

function addLangParameter(request) {
    const newUrl = request.url + "?lang=" + lang;
    return new Request(newUrl, request);
}

который, похоже, копирует большинство атрибутов старого запроса, но, похоже, не сохраняет body старого запроса. Например,

const request1 = new Request("/", { method: "POST", body: "test" });
const request2 = new Request("/new", request1);
request2.text().then(body => console.log(body));

Я ожидал бы log "test", но вместо этого он записывает пустую строку, потому что тело не копируется.

Нужно ли мне делать что-то более явное, чтобы правильно копировать все атрибуты, или есть хороший ярлык, который сделает что-то разумное для меня?

Я использую github/fetch polyfill, но протестировал с помощью как polyfill, так и собственной fetch реализации в последнем Chrome.

Ответ 1

Похоже, ваш лучший выбор - прочитать тело, используя интерфейс Body, который выполняет Requests:

https://fetch.spec.whatwg.org/#body

Это можно сделать только асинхронно, так как основная операция "потреблять тело" всегда читается асинхронно и возвращает обещание. Что-то вроде этого должно работать:

const request = new Request('/old', { method: 'GET' });
const bodyP = request.headers.get('Content-Type') ? request.blob() : Promise.resolve(undefined);
const newRequestP =
  bodyP.then((body) =>
    new Request('/new', {
      method: request.method,
      headers: request.headers,
      body: body,
      referrer: request.referrer,
      referrerPolicy: request.referrerPolicy,
      mode: request.mode,
      credentials: request.credentials,
      cache: request.cache,
      redirect: request.redirect,
      integrity: request.integrity,
    })
  );

После этого newRequestP будет обещанием, которое разрешит требуемый запрос. К счастью, выборка является асинхронной, так что ваша обертка не должна быть значительно затруднена этим.

(Примечание. Чтение тела с использованием .blob() у запроса, у которого нет тела, похоже, возвращает объект Blob нулевой длины, но неверно указывать любое тело, даже нулевую длину, на GET или HEAD. Я считаю, что проверка того, что исходный запрос имеет Content-Type, является точным прокси-сервером для того, имеет ли он тело, что нам действительно нужно определить.)