确保您的服务工知道在请求部分响应时该怎么做。
某些 HTTP 请求包含 Range:
标头,表示应仅返回完整资源的一部分。它们通常用于流式传输音频或视频内容,以便按需加载较小的媒体内容块,而不是一次请求整个远程文件。
服务工作线程是位于 Web 应用和网络之间的 JavaScript 代码,可能会拦截传出的网络请求并为其生成响应。
从历史上看,范围请求和服务工作器无法很好地协同工作。您必须采取特殊措施,以避免 Service Worker 出现不良结果。幸运的是,这种情况正在开始改变。在表现出正确行为的浏览器中,范围请求在通过 Service Worker 时会“正常运行”。
具体是什么问题?
假设有一个服务工件,其中包含以下 fetch
事件监听器,该监听器会接收每个传入请求并将其传递给网络:
self.addEventListener('fetch', (event) => {
// The Range: header will not pass through in
// browsers that behave incorrectly.
event.respondWith(fetch(event.request));
});
在存在错误行为的浏览器中,如果 event.request
包含 Range:
标头,系统会静默地舍弃该标头。远程服务器收到的请求根本不会包含 Range:
。这不一定会破坏任何内容,因为从技术层面讲,服务器可以返回包含 200
状态代码的完整响应正文,即使原始请求中存在 Range:
标头也是如此。但从浏览器的角度来看,这会导致传输的数据量超出严格需要的范围。
了解此行为的开发者可以通过明确检查是否存在 Range:
标头来规避此问题,如果存在,则不调用 event.respondWith()
。这样一来,服务工作器便会从响应生成流程中有效移除自身,并改用知道如何保留范围请求的默认浏览器网络逻辑。
self.addEventListener('fetch', (event) => {
// Return without calling event.respondWith()
// if this is a range request.
if (event.request.headers.has('range')) {
return;
}
event.respondWith(fetch(event.request));
});
不过,可以肯定的是,大多数开发者都不知道需要这样做。而且,我们也不清楚为什么需要这样做。最终,这一限制是由于浏览器需要跟上底层规范的变化,后者增加了对此功能的支持。
修复了哪些问题?
行为正常的浏览器会在将 event.request
传递给 fetch()
时保留 Range:
标头。这意味着,我最初示例中的 Service Worker 代码将允许远程服务器查看 Range:
标头(如果它是由浏览器设置的):
self.addEventListener('fetch', (event) => {
// The Range: header will pass through in browsers
// that behave correctly.
event.respondWith(fetch(event.request));
});
现在,服务器有机会正确处理范围请求,并返回包含 206
状态代码的部分响应。
哪些浏览器的行为正常?
最新版本的 Safari 具有正确的功能。从 87 版开始,Chrome 和 Edge 也会正常运行。
截至 2020 年 10 月,Firefox 尚未修复此行为,因此在将服务工件的代码部署到正式版时,您可能仍需要考虑这一点。
如需确认给定浏览器是否已更正此行为,最好的方法是查看“网站平台测试”信息中心的“在网络请求中添加范围标头”行。
如何从缓存中提供范围请求?
Service Worker 不仅可以将请求传递给网络,还可以执行更多操作。一个常见的用例是将音频和视频文件等资源添加到本地缓存中。然后,Service Worker 可以从该缓存中执行请求,完全绕过网络。
所有浏览器(包括 Firefox)都支持在 fetch
处理程序中检查请求、检查是否存在 Range:
标头,然后使用来自缓存的 206
响应在本地执行请求。不过,要编写服务工件代码来正确解析 Range:
标头并仅返回完整缓存响应的相应部分,并非易事。
幸运的是,需要帮助的开发者可以求助 Workbox,这是一组可简化常见服务工作器用例的库。workbox-range-request module
会实现直接从缓存中传送部分响应所需的所有逻辑。如需查看此用例的完整方案,请参阅 Workbox 文档。
此帖子的主打图片由 Unsplash 用户 Natalie Rhea Riggs 提供。