我有一个 ServiceWorker 可以拦截 Fetch 请求。有时,获取请求注定会失败,因此 ServiceWorker 应该在它到达网络之前取消它。
我的代码如下所示:
async function onFetch(event) {
const { request } = event;
const result = handleRequest(request)
.then(someStuff)
.catch((err) => {
const ctrl = new AbortController();
ctrl.abort('request is doomed');
const cancellation = fetch(request, { signal: ctrl.signal });
// cancellation is correctly an AbortError DOMException
// event.respondWith(cancellation); // results in net::ERR_FAILED
return cancellation;
});
return event.waitUntil(result);
}
在没有 Service Worker 的情况下进行的快速实验表明这应该可行:
const ctrl = new AbortController();
ctrl.abort('request is doomed');
fetch('http://localhost', { signal: ctrl: signal });
在上述实验中,DevTools Network面板中的请求被标记为“已取消”,并且没有发出网络请求。
但是,在ServiceWorker中,请求不会被取消(中止信号被完全忽略)并且它会发送到网络。
如果您提供
event.respondWith
一个新的 Response
(或 PromiseLike<Response>
),并且不获取 event.request
,那么您实际上取消了原始请求。
根据您提供的
Response
,这可能会产生方便的副作用,例如取消导航。例如,您可以使用方法 POST
: 取消任何表单提交发出的获取请求
self.addEventListener('fetch', (event) => {
if (event.request.method === 'POST' && event.request.mode === 'navigate') {
event.respondWith(new Response(null, { status: 204 }));
}
});
204 No Content
来阻止此处的导航。根据上下文,其他状态代码可能对您的用例有更方便的语义。