Service Worker 和非 CORS(不透明)响应
Service Worker 拦截非 CORS 响应并使用 Cache API 将其缓存为不透明响应。这些响应会以状态码 0 被拦截。由于其不透明特性,JavaScript 无法检查这些响应,使得 Service Worker 无法根据状态码、头部或其他条件对这些响应进行有条件缓存。
Workbox 缓存策略和不透明响应
Workbox 建议(并默认为)在“优先缓存”策略中仅缓存状态码为 200 的响应。这意味着在这种情况下,不透明响应不会被缓存。然而,“stale-while-revalidate”(旧缓存优先,再重新验证)策略会缓存状态码为 200 和 0 的响应。
将不透明响应用作页面资源
通过 HTML 元素的 src
属性加载的资源(如 img
、video
等)默认作为 no-cors
请求发出,因此它们的响应会是不透明的。在浏览器允许非 CORS 跨域资源的情况下,不透明响应可用作网页资源。以下是可以使用非 CORS 跨域资源(即不透明响应)的元素子集:
<script>
<link rel="stylesheet">
-
<img>
、<video>
和<audio>
-
<object>
和<embed>
<iframe>
可以通过添加 crossorigin
属性将这些元素的请求变为 CORS。
需要注意的是,不透明响应对字体资源无效。
要确定是否可以将不透明响应用作页面上的特定类型资源,可以查阅相关规范。例如,HTML 规范解释了非 CORS 跨域(即不透明)响应可用于 <script>
元素,但存在一些限制,以防止泄露错误信息。
不透明响应的限制
- 检查:Service Worker(或 JavaScript 通常情况下)无法检查不透明响应,包括其大小。
-
访问头部和主体:不能从
Response
类的大多数属性中获取有效信息,例如headers
,也不能调用Body
接口中的json()
或text()
等方法。
重写 no-cors 请求以检查不透明响应
作为应对不透明响应限制的解决方法,可以在 Service Worker 中拦截 no-cors
请求并将其重写为 CORS 请求。这样可以检查并根据状态码有条件地缓存这些响应。
例如,使用 Workbox 可以这样实现:
const forceCorsRequestPlugin = {
requestWillFetch: async ({ request }) => {
// 如果请求不是 no-cors,则不修改请求(例如包含凭证的请求)。
if (request.mode !== "no-cors") {
return request;
}
return new Request(request, { mode: "cors", credentials: "omit" });
},
};
不透明响应与 Cache Storage API
关于 Cache Storage API(Service Worker 用于处理缓存的 API)行为的一个小细节:当使用不透明响应时,add()
和 addAll()
方法会拒绝状态码不在 2XX 范围内的响应。因此,使用 add()
或 addAll()
方法将无法将不透明响应添加到缓存中。要解决此问题,可以显式执行 fetch()
请求,然后使用 put()
方法存储不透明响应。
不透明响应与 navigator.storage API
为防止跨域信息泄露,浏览器在计算存储配额限制时会对不透明响应的大小进行显著填充。在 Google Chrome 中,单个缓存的不透明响应对总体存储使用的最低贡献大小约为 7 MB。请记住这一点,以便在决定缓存多少不透明响应时避免超出存储配额限制,这样可能会比预期更快达到上限。