Service Worker 离线无法缓存Post请求的问题解决
许多非REST API甚至可以用于读取数据的POST请求:典型的例子是graphql、soap和其他rpcpapi。但是,Post请求不能在一个现成的渐进式Web应用程序中缓存和脱机使用。浏览器的缓存API不会接受它们。下面是一个在IncedB中使用自定义缓存的解决方案。
幸运的是Service Worker可以截获任何异步请求,因此我们处理POST请求没有问题。缺少的是在离线时缓存它们并检索相应的响应的可能性。
在下面的示例中,我们将在indexeddb中创建一个简单的缓存,并将其用作回退(networkfirst策略)。注意,我们将使用整个请求的字符串化版本作为缓存键。这是非常安全的,但会为具有长主体和/或头的请求生成大密钥。如果这对您来说是一个问题,您可以选择另一种方法来构建键,或者只使用lz字符串或类似的库来压缩它。
一句警告
当然,有一些原因可以解释为什么post请求不属于当前Service Worker规范的一部分。以下是其中一些:
从理论上讲,POSTs将修改数据。当然,您不能使用正在尝试修改的数据缓存版本。所以不要缓存任何类型的修改请求!
这些API使用文章进行阅读,通常不会为每个可能的结果提供单独的URL。相反,它们只有一个URL来检索不同的数据集,并接受请求主体中某种类型的查询。这意味着,您不能缓存基于URL的响应(就像缓存API一样),而是需要使用自己的组合键,包括主体和可能的一些头。
所以在将post请求缓存到您的数据之前,请仔细考虑!如果您正在寻找一种使修改post请求离线工作的方法,我建议您查看用于延迟发布的Mozilla代码配方。
如何在服务工作进程中缓存发布请求
对于缓存所有post请求的工作示例,请将此代码放在serviceworker.js中。我在这里使用dexie.js访问indexeddb,但是任何其他方法也可以。
importScripts('your/path/to/dexie/dist/dexie.min.js');
// Listen to fetch requests
self.addEventListener('fetch', function(event) {
// We will cache all POST requests, but in the real world, you will probably filter for
// specific URLs like if(... || event.request.url.href.match(...))
if(event.request.method === "POST"){
// Init the cache. We use Dexie here to simplify the code. You can use any other
// way to access IndexedDB of course.
var db = new Dexie("post_cache");
db.version(1).stores({
post_cache: 'key,response,timestamp'
})
event.respondWith(
// First try to fetch the request from the server
fetch(event.request.clone())
.then(function(response) {
// If it works, put the response into IndexedDB
cachePut(event.request.clone(), response.clone(), db.post_cache);
return response;
})
.catch(function() {
// If it does not work, return the cached response. If the cache does not
// contain a response for our request, it will give us a 503-response
return cacheMatch(event.request.clone(), db.post_cache);
})
);
}
})
/**
* Serializes a Request into a plain JS object.
*
* @param request
* @returns Promise
*/
function serializeRequest(request) {
var serialized = {
url: request.url,
headers: serializeHeaders(request.headers),
method: request.method,
mode: request.mode,
credentials: request.credentials,
cache: request.cache,
redirect: request.redirect,
referrer: request.referrer
};
// Only if method is not `GET` or `HEAD` is the request allowed to have body.
if (request.method !== 'GET' && request.method !== 'HEAD') {
return request.clone().text().then(function(body) {
serialized.body = body;
return Promise.resolve(serialized);
});
}
return Promise.resolve(serialized);
}
/**
* Serializes a Response into a plain JS object
*
* @param response
* @returns Promise
*/
function serializeResponse(response) {
var serialized = {
headers: serializeHeaders(response.headers),
status: response.status,
statusText: response.statusText
};
return response.clone().text().then(function(body) {
serialized.body = body;
return Promise.resolve(serialized);
});
}
/**
* Serializes headers into a plain JS object
*
* @param headers
* @returns object
*/
function serializeHeaders(headers) {
var serialized = {};
// `for(... of ...)` is ES6 notation but current browsers supporting SW, support this
// notation as well and this is the only way of retrieving all the headers.
for (var entry of headers.entries()) {
serialized[entry[0]] = entry[1];
}
return serialized;
}
/**
* Creates a Response from it's serialized version
*
* @param data
* @returns Promise
*/
function deserializeResponse(data) {
return Promise.resolve(new Response(data.body, data));
}
/**
* Saves the response for the given request eventually overriding the previous version
*
* @param data
* @returns Promise
*/
function cachePut(request, response, store) {
var key, data;
getPostId(request.clone())
.then(function(id){
key = id;
return serializeResponse(response.clone());
}).then(function(serializedResponse) {
data = serializedResponse;
var entry = {
key: key,
response: data,
timestamp: Date.now()
};
store
.add(entry)
.catch(function(error){
store.update(entry.key, entry);
});
});
}
/**
* Returns the cached response for the given request or an empty 503-response for a cache miss.
*
* @param request
* @return Promise
*/
function cacheMatch(request) {
return getPostId(request.clone())
.then(function(id) {
return store.get(id);
}).then(function(data){
if (data) {
return deserializeResponse(data.response);
} else {
return new Response('', {status: 503, statusText: 'Service Unavailable'});
}
});
}
/**
* Returns a string identifier for our POST request.
*
* @param request
* @return string
*/
function getPostId(request) {
return new Promise((resolve, reject) => {
return JSON.stringify(serializeRequest(request.clone()));
});
}
Service Worker 离线无法缓存Post请求的问题解决的更多相关文章
- 前端存储 (5) - service worker 离线存储
service worker 离线存储 简介: 一般的网站 在我们无法访问的 时候 一般 回出现 如下 该网页无法访问 service worker 构建的网站不会出现这个错误,因为所有的 请求都是先 ...
- Service Worker和HTTP缓存
很多人,包括我自己,初看Service Worker多一个Cache Storage的时候,就感觉跟HTTP长缓存没什么区别. 例如大家讲的最多的Service Worker能让网页离线使用,但熟悉H ...
- 浏览器缓存和Service Worker
浏览器缓存和Service Worker @billshooting 2018-05-06 字数 6175 Follow me on Github 标签: BOM 1. 传统的HTTP浏览器缓存策略 ...
- [转] service worker初探:超级拦截器与预缓存
在2014年,W3C公布了service worker的草案,service worker提供了很多新的能力,使得web app拥有与native app相同的离线体验.消息推送体验. service ...
- Service Worker 缓存文件处理
交代背景 前段时间升级了一波Google Chrome,发现我的JulyNovel站点Ctrl+F5也刷新不了,后来发现是新的Chrome已经支持Service Worker,而我的JulyNovel ...
- 渐进式web应用开发---service worker (二)
阅读目录 1. 创建第一个service worker 及环境搭建 2. 使用service worker 对请求拦截 3. 从web获取内容 4. 捕获离线请求 5. 创建html响应 6. 理解 ...
- 渐进式web应用开发---Service Worker 与页面通信(七)
_ 阅读目录 一:页面窗口向 service worker 通信 二:service worker 向所有打开的窗口页面通信 三:service worker 向特定的窗口通信 四:学习 Messag ...
- 浅析Service Worker
一.service worker是什么? 平常浏览器窗口中跑的页面运行的是主JavaScript线程,DOM和window全局变量都是可以访问的. Service Worker是走的另外的线程,可以理 ...
- service worker介绍
原文:Service workers explained 译者:neal1991 welcome to star my articles-translator, providing you advan ...
随机推荐
- WEB学习笔记10-高可读性的HTML之HTML 语义化
实现如下所示工具栏: 做到标签语义化,首先要尽量减少使用<div>和<span>这两个标签. 分析:这是一个包含5个无序操作项的工具栏,因此应该使用符合语义的<ul> ...
- RN集成echarts4图表组件react-native-secharts(转载)
一个webview封装的图表组件.基于百度echarts4,相比native-echarts有echarts自带对象支持,例如渐变色等,用法与官网相同用法. echarts version 4.2.0 ...
- 百战程序员——Spring框架
什么是容器,我们学过了哪些容器,Spring与我们之前学习的容器有哪些异同点? 容器可以管理对象的生命周期.对象与对象之间的依赖关系,您可以使用一个配置文件(通常是XML),在上面定义好对象的名称.如 ...
- vue 使用canvas仿芝麻分信用表
如图所示: 画布组件:dashboard.vue <template> <div> <canvas ref="canvas" v-if="c ...
- Exp6 信息搜集与漏洞扫描
Exp6 信息搜集与漏洞扫描 1.实践目标 掌握信息搜集的最基础技能与常用工具的使用方法. 2.实践内容 (1)各种搜索技巧的应用 (2)DNS IP注册信息的查询 (3)基本的扫描技术:主机发现. ...
- React 组件间通信介绍
React 组件间通信方式简介 React 组件间通信主要分为以下四种情况: 父组件向子组件通信 子组件向父组件通信 跨级组件之间通信 非嵌套组件间通信 下面对这四种情况分别进行介绍: 父组件向子 ...
- cordova插件新的窗口实例打开连接: cordova-plugin-inappbrowser
1. 添加插件:cordova plugin add cordova-plugin-inappbrowser : 2. InAppBrowser可以使用新的窗口实例打开连接,提供了地址栏的显示隐藏, ...
- c#实现识别图片上的验证码数字
这篇文章主要介绍了c#实现识别图片上的验证码数字的方法,本文给大家汇总了2种方法,有需要的小伙伴可以参考下. ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 ...
- JDK11&12 新特性学习
- Hadoop MapReduce2.0(Yarn)
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/cqboy1991/article/details/25056283 MapReduce2.0(Yar ...