【问题标题】:Cannot construct a Request with a Request whose mode is 'navigate' and a non-empty RequestInit无法使用模式为“导航”的请求和非空 RequestInit 构造请求
【发布时间】:2022-01-23 07:32:27
【问题描述】:

考虑这个示例index.html 文件。

<!DOCTYPE html>
<html><head><title>test page</title>
<script>navigator.serviceWorker.register('sw.js');</script>
</head>
<body>
<p>test page</p>
</body>
</html>

使用此 Service Worker,旨在从缓存中加载,然后在必要时回退到网络。

cacheFirst = (request) => {
    var mycache;
    return caches.open('mycache')
        .then(cache => {
            mycache = cache;
            cache.match(request);
        })
        .then(match => match || fetch(request, {credentials: 'include'}))
        .then(response => {
            mycache.put(request, response.clone());
            return response;
        })
}

addEventListener('fetch', event => event.respondWith(cacheFirst(event.request)));

这在 Chrome 62 上严重失败。刷新 HTML 根本无法在浏览器中加载,出现“无法访问此站点”错误;我必须改变刷新才能摆脱这种破碎状态。在控制台中,它说:

未捕获(承诺中)类型错误:无法在“ServiceWorkerGlobalScope”上执行“获取”:无法使用模式为“导航”且非空 RequestInit 的请求构造请求。

“构造一个请求”?!我不是在构建请求。我正在使用事件的请求,未修改。我在这里做错了什么?

【问题讨论】:

  • 您的 Service Worker 是否已正确安装和注册?
  • 它必须注册,否则刷新时不会炸毁页面!此外,它在开发工具应用程序选项卡中显示为正在运行/已注册。

标签: javascript service-worker


【解决方案1】:

根据进一步的研究,事实证明,当我fetch(request, {credentials: 'include'})时,我正在构造一个请求!

每当您将选项对象传递给fetch 时,该对象就是RequestInit,并且在您这样做时它会创建一个新的Request 对象。而且,呃,显然你不能让fetch()navigate 模式下创建一个新的Request 和一个非空的RequestInit

在我的情况下,事件导航 Request 已经允许凭据,因此解决方法是将 fetch(request, {credentials: 'include'}) 转换为 fetch(request)

由于this Google documentation article,我误以为我需要{credentials: 'include'}

当您使用 fetch 时,默认情况下,请求不会包含 cookie 等凭据。如果您需要凭据,请致电:

fetch(url, {
  credentials: 'include'
})

只有在您传递 fetch 一个 URL 时才会如此,就像在代码示例中所做的那样。如果你手头有一个Request 对象,就像我们通常在 Service Worker 中所做的那样,Request 知道它是否要使用凭据,所以fetch(request) 将正常使用凭据。

【讨论】:

    【解决方案2】:

    https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker

    var networkDataReceived = false;
    // fetch fresh data
    var networkUpdate = fetch('/data.json').then(function(response) {
      return response.json();
    }).then(function(data) {
      networkDataReceived = true;
      updatePage(data);
    });
    
    // fetch cached data
    caches.match('mycache').then(function(response) {
      if (!response) throw Error("No data");
      return response.json();
    }).then(function(data) {
      // don't overwrite newer network data
      if (!networkDataReceived) {
        updatePage(data);
      }
    }).catch(function() {
      // we didn't get cached data, the network is our last hope:
      return networkUpdate;
    }).catch(showErrorMessage).then(console.log('error');
    

    您正在尝试做的事情的最佳示例,尽管您必须相应地更新您的代码。 web示例取自Cache然后network。

    for the service worker:
    self.addEventListener('fetch', function(event) {
      event.respondWith(
        caches.open('mycache').then(function(cache) {
          return fetch(event.request).then(function(response) {
            cache.put(event.request, response.clone());
            return response;
          });
        })
      );
    });
    

    【讨论】:

      【解决方案3】:

      问题

      我在尝试为各种不同的资产覆盖 fetch 时遇到了这个问题。 navigate 模式设置为初始 Request 获取 index.html(或其他 html)文件;我希望将相同的缓存规则应用于它,就像我希望应用于其他几个静态资产一样。

      这是我希望能够完成的两件事:

      1. 在获取静态资产时,有时我希望能够覆盖url,这意味着我想要类似:fetch(new Request(newUrl))
      2. 同时,我希望按照发件人的意图提取它们;这意味着我想将fetch 的第二个参数(即错误消息中提到的RequestInit 对象)设置为originalRequest 本身,如下所示:fetch(new Request(newUrl), originalRequest)

      但是第二部分不适用于navigate 模式下的请求(即初始html 文件);同时,正如其他人所解释的那样,它不需要它,因为它已经保留了它的 cookie、凭据等。

      解决方案

      这是我的解决方法:一个多才多艺的fetch...

      1. 可以覆盖网址
      2. 可以覆盖RequestInit配置对象
      3. 适用于 navigate 以及任何其他请求
      function fetchOverride(originalRequest, newUrl) {
        const fetchArgs = [new Request(newUrl)];
        if (request.mode !== 'navigate') {
          // customize the request only if NOT in navigate mode
          //    (since in "navigate" that is not allowed)
          fetchArgs.push(request);
        }
      
        return fetch(...fetchArgs);
      }
      
      

      【讨论】:

        【解决方案4】:

        在我的例子中,我正在从服务工作者中的序列化表单构造一个请求(以处理失败的 POST)。在原始请求中,它具有mode 属性集,该属性是只读的,因此在重构请求之前,请删除mode 属性:

        delete serializedRequest["mode"];
        request = new Request(serializedRequest.url, serializedRequest);
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-03-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-18
          • 1970-01-01
          • 1970-01-01
          • 2011-02-06
          相关资源
          最近更新 更多