如何用 service worker 增加响应体的 header 字段

本文介绍了在前端用 service worker 增加 header 字段。不用配置 nginx, 就可以实现CORP(跨源隔离)。

关键词

service worker、proxy、 拦截、fetch、 CORP

问题背景

之前实现了 web 端视频编辑的功能(详见(如何用 ffmpeg + canvas 写一个在线可视化音视频编辑工具),用了@ffmpeg/ffmpeg库。 然后遇到一个问题,ffmpeg 使用到了 SharedArrayBuffer, 但是 SharedArrayBuffer 由于安全问题,会有一些限制。如果要在页面中使用 SharedArrayBuffer,需要设置页面为跨域隔离。开启方式为在页面中设置两个请求头,可以设置 CORP

http 复制代码
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Opener-Policy: same-origin

本来项目的静态页面是部署在 Github 的,但是 Github 无法设置 header,所以只能将网页部署在 vercel 上。 在进行文章的分享后,有大佬在评论区提供了一个新的解决方案,来让 github 就可以访问。方案就是通过 service worker 进行 fetch的拦截, 来实现改写 header 的目的,这确实是一个非常好的方案。

service worker

首先来简单的介绍一下 service worker

概念

Service Worker是一个强大的技术,它为我们提供了在浏览器中运行后台脚本的能力。通过使用Service Worker,我们可以实现离线缓存、推送通知和拦截网络请求等功能,为用户提供更好的体验。 其中,拦截网络请求并修改请求头是Service Worker的一个重要应用场景,它使我们能够动态地控制和定制请求的行为。

修改请求头

我们来看如何实现 header 的拦截,分为两个步骤:

  • 注册 service worker
  • 劫持 fetch, 修改 header

首先我们需要注册 service worker

js 复制代码
// 注册Service Worker
if ('serviceWorker' in navigator) {
  navigator.serviceWorker.register('/service-worker.js')
    .then(function(registration) {
      console.log('Service Worker 注册成功:', registration);
    })
    .catch(function(error) {
      console.log('Service Worker 注册失败:', error);
    });
}

然后我们在fetch事件中拦截了请求。在拦截的处理函数中,我们使用fetch函数来获取原始的响应对象。然后,我们克隆了响应对象,并使用Headers对象来修改请求头,添加了一个自定义的请求头字段。最后,我们使用修改后的请求头创建了一个新的响应对象,并将其返回。

javascript 复制代码
// service-worker.js

// 拦截请求并修改header
self.addEventListener('fetch', function(event) {
  event.respondWith(
    fetch(event.request)
      .then(function(response) {
        // 克隆响应对象
        const clonedResponse = response.clone();
        
        // 修改请求头
        const modifiedHeaders = new Headers(clonedResponse.headers);
        modifiedHeaders.set('Custom-Header', 'Custom Value');
        
        // 创建新的响应对象,将修改后的请求头应用于其中
        const modifiedResponse = new Response(clonedResponse.body, {
          status: clonedResponse.status,
          statusText: clonedResponse.statusText,
          headers: modifiedHeaders
        });
        
        return modifiedResponse;
      })
      .catch(function(error) {
        console.log('请求失败:', error);
      })
  );
});

打开浏览器的控制台,可以看到响应请求已经增加了预期的 header 字段。

按照这个思路,我们只需要在 header 中增加CORP 需要的三个字段就可以了。

js 复制代码
 // 修改请求头
const modifiedHeaders = new Headers(clonedResponse.headers);
modifiedHeaders.set('Cross-Origin-Opener-Policy', 'same-origin');
modifiedHeaders.set('Cross-Origin-Embedder-Policy', 'require-corp');
modifiedHeaders.set('Cross-Origin-Resource-Policy', 'cross-origin');

这样子,我们的页面功能就正常了。

现成的轮子

在分析完原理以后,我们继续在 github 中搜索,发现别人也有同样的问题,找到了一些现成的轮子。所以觉得不再自己实现了。

mini-coi 这个包的使用很简单,先下载一个 service worker 文件,然后在 html 文件中引入就好了, 基本不需要配置。 看一下它的源码,实现方式也基本一致,是对 fetch 做了劫持,只是做了一些细节的处理,比如不对错误请求做处理。

js 复制代码
addEventListener('fetch', e => {
      const { request: r } = e;
      if (r.cache === 'only-if-cached' && r.mode !== 'same-origin') return;
      e.respondWith(fetch(r).then(r => {
        const { body, status, statusText } = r;
        if (!status || status > 399) return r;
        const h = new Headers(r.headers);
        h.set('Cross-Origin-Opener-Policy', 'same-origin');
        h.set('Cross-Origin-Embedder-Policy', 'require-corp');
        h.set('Cross-Origin-Resource-Policy', 'cross-origin');
        return new Response(body, { status, statusText, headers: h });
      }));
    });

sw 的其他应用场景

继续搜索 Github ,还找到了很多其他有意思的场景。

设置请求超时

Service-Workers-Fetch-Timeout 监听 fetch 请求,如果请求时间超过 2s, 则会返回页面的请求是一个超时请求。

js 复制代码
function timeout(delay) {
    return new Promise(function(resolve, reject) {
        setTimeout(function(){
          resolve(new Response('', {
              status: 408,
              statusText: 'Request timed out.'
          }));
        }, delay);
    });
}

self.addEventListener('fetch', function(event) {
    // Only fetch JavaScript files for now
    if (/\.js$/.test(event.request.url)) {
      event.respondWith(Promise.race([timeout(2000), fetch(event.request.url)]));
    } else {
      event.respondWith(fetch(event.request));
    }
});

代码压缩

service-worker-brotli 通过 Service Worker,在尚不支持BCD的CDN上启用 Brotli(一种高级压缩算法,类似 gzip)。

js 复制代码
self.addEventListener("fetch", (event) => {
    const { request } = event;
    const url = new URL(request.url);

    if (!url.protocol.startsWith("http")) return;
    if (request.method !== "GET") return;
    if (request.mode === "websocket") return;

    event.respondWith(handle_fetch(request));
});

参考

mini-coi

Service-Workers-Fetch-Timeout

service-worker-brotli

前端性能优化(二):资源优化

相关推荐
耶啵奶膘35 分钟前
uniapp-是否删除
linux·前端·uni-app
王哈哈^_^2 小时前
【数据集】【YOLO】【目标检测】交通事故识别数据集 8939 张,YOLO道路事故目标检测实战训练教程!
前端·人工智能·深度学习·yolo·目标检测·计算机视觉·pyqt
cs_dn_Jie3 小时前
钉钉 H5 微应用 手机端调试
前端·javascript·vue.js·vue·钉钉
开心工作室_kaic3 小时前
ssm068海鲜自助餐厅系统+vue(论文+源码)_kaic
前端·javascript·vue.js
有梦想的刺儿4 小时前
webWorker基本用法
前端·javascript·vue.js
cy玩具4 小时前
点击评论详情,跳到评论页面,携带对象参数写法:
前端
清灵xmf4 小时前
TypeScript 类型进阶指南
javascript·typescript·泛型·t·infer
小白学大数据4 小时前
JavaScript重定向对网络爬虫的影响及处理
开发语言·javascript·数据库·爬虫
qq_390161775 小时前
防抖函数--应用场景及示例
前端·javascript
334554325 小时前
element动态表头合并表格
开发语言·javascript·ecmascript