How to alter the headers of a Request?

2019-01-18 06:21发布

问题:

Is it possible to alter the headers of the Request object that is received by the fetch event?

Two attempts:

  1. Modify existing headers:

    self.addEventListener('fetch', function (event) {
      event.request.headers.set("foo", "bar");
      event.respondWith(fetch(event.request));
    });
    

    Fails with Failed to execute 'set' on 'Headers': Headers are immutable.

  2. Create new Request object:

    self.addEventListener('fetch', function (event) {
      var req = new Request(event.request, {
        headers: { "foo": "bar" }
      });
      event.respondWith(fetch(req));
    });
    

    Fails with Failed to construct 'Request': Cannot construct a Request with a Request whose mode is 'navigate' and a non-empty RequestInit.

(See also How to alter the headers of a Response?)

回答1:

Creating a new request object works as long as you set all the options:

// request is event.request sent by browser here 
var req = new Request(request.url, {
    method: request.method,
    headers: request.headers,
    mode: 'same-origin', // need to set this properly
    credentials: request.credentials,
    redirect: 'manual'   // let browser handle redirects
});

You cannot use the original mode if it is navigate (that's why you were getting an exception) and you probably want to pass redirection back to browser to let it change its URL instead of letting fetch handle it.

Make sure you don't set body on GET requests - fetch does not like it, but browsers sometimes generate GET requests with the body when responding to redirects from POST requests. fetch does not like it.



回答2:

Have you tried with a solution similar to the one in the question you mention (How to alter the headers of a Response?)?

In the Service Worker Cookbook, we're manually copying Request objects to store them in IndexedDB (https://serviceworke.rs/request-deferrer_service-worker_doc.html). It's for a different reason (we wanted to store them in a Cache, but we can't store POST requests because of https://github.com/slightlyoff/ServiceWorker/issues/693), but it should be applicable for what you want to do as well.

// Serialize is a little bit convolved due to headers is not a simple object.
function serialize(request) {
  var headers = {};
  // `for(... of ...)` is ES6 notation but current browsers supporting SW, support this
  // notation as well and this is the only way of retrieving all the headers.
  for (var entry of request.headers.entries()) {
    headers[entry[0]] = entry[1];
  }
  var serialized = {
    url: request.url,
    headers: headers,
    method: request.method,
    mode: request.mode,
    credentials: request.credentials,
    cache: request.cache,
    redirect: request.redirect,
    referrer: request.referrer
  };



  // Only if method is not `GET` or `HEAD` is the request allowed to have body.
  if (request.method !== 'GET' && request.method !== 'HEAD') {
    return request.clone().text().then(function(body) {
      serialized.body = body;
      return Promise.resolve(serialized);
    });
  }
  return Promise.resolve(serialized);
}

// Compared, deserialize is pretty simple.
function deserialize(data) {
  return Promise.resolve(new Request(data.url, data));
}