许多非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,但是任何其他方法也可以。

https://dexie.org/

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请求的问题解决的更多相关文章

  1. 前端存储 (5) - service worker 离线存储

    service worker 离线存储 简介: 一般的网站 在我们无法访问的 时候 一般 回出现 如下 该网页无法访问 service worker 构建的网站不会出现这个错误,因为所有的 请求都是先 ...

  2. Service Worker和HTTP缓存

    很多人,包括我自己,初看Service Worker多一个Cache Storage的时候,就感觉跟HTTP长缓存没什么区别. 例如大家讲的最多的Service Worker能让网页离线使用,但熟悉H ...

  3. 浏览器缓存和Service Worker

    浏览器缓存和Service Worker @billshooting 2018-05-06 字数 6175 Follow me on Github 标签: BOM 1. 传统的HTTP浏览器缓存策略 ...

  4. [转] service worker初探:超级拦截器与预缓存

    在2014年,W3C公布了service worker的草案,service worker提供了很多新的能力,使得web app拥有与native app相同的离线体验.消息推送体验. service ...

  5. Service Worker 缓存文件处理

    交代背景 前段时间升级了一波Google Chrome,发现我的JulyNovel站点Ctrl+F5也刷新不了,后来发现是新的Chrome已经支持Service Worker,而我的JulyNovel ...

  6. 渐进式web应用开发---service worker (二)

    阅读目录 1. 创建第一个service worker 及环境搭建 2. 使用service worker 对请求拦截 3. 从web获取内容 4. 捕获离线请求 5. 创建html响应 6. 理解 ...

  7. 渐进式web应用开发---Service Worker 与页面通信(七)

    _ 阅读目录 一:页面窗口向 service worker 通信 二:service worker 向所有打开的窗口页面通信 三:service worker 向特定的窗口通信 四:学习 Messag ...

  8. 浅析Service Worker

    一.service worker是什么? 平常浏览器窗口中跑的页面运行的是主JavaScript线程,DOM和window全局变量都是可以访问的. Service Worker是走的另外的线程,可以理 ...

  9. service worker介绍

    原文:Service workers explained 译者:neal1991 welcome to star my articles-translator, providing you advan ...

随机推荐

  1. 简单几步,教你学会PHP,新手必看!

    学习php的方法,学东西,永远不要妄想有速成这一说,告诉你了一个方式,但是缺少努力这一环节,那也是白搭.掌握好的学习方法非常必要,看看这篇如何学习PHP培训的方法,在此提醒一下大家,PHP不像别的科目 ...

  2. 【转】vs2017快捷键大全

    最近接触到.net,用到vs2017,由于以前用的是eclipse,没有用过vs,加上又是日语版的,给工作带来了很多不便,于是网上查了vs2017的快捷键. 项目相关的快捷键 Ctrl + Shift ...

  3. C#action和func的使用

    以前我都是通过定义一个delegate来写委托的,但是最近看一些外国人写的源码都是用action和func方式来写,当时感觉对这很陌生所以看起源码也觉得陌生,所以我就花费时间来学习下这两种方式,然后发 ...

  4. Heap Sort - recursion

    Heap Sort  Build a max heap using exsiting array, which is called Heapify Swap root with the last el ...

  5. cmd中运行maven -v提示JAVA_HOME的配置问题解决办法

    问题描述: 在安装maven之后,输入:mvn --version进行查询,结果是: The JAVA_HOME environment variable is not defined correct ...

  6. 常见Soc平台图形内存管理学习笔记

    硬件编解码.硬件图像scale等过程,是在专有的硬件单元里进行,其使用的内存也是专有的内存,这种内存多是SoC中图形内存.如此方便与硬件加速图形渲染.图像显示.硬件图像加速处理等功能相交互. 上述过程 ...

  7. 开发Canvas 绘画应用(三):实现对照绘画

    需求分析 在我的毕设中,提出了视图引导的概念,由两部分功能组成: (1)可以对照着图片进行绘画,即将图片以半透明的方式呈现在绘图板上,然后用户可以对照着进行绘画: (2)可以直接将简笔画图片直接拖拽到 ...

  8. 2.7 while 、for 循环控制语句

    一.while语句: 在程序中,需要重复性的做某件事: 1.1.1 while: public class Test{ public static void main(String[] args){ ...

  9. html中头meta信息

    一.页面关键字 网站关键字:用户通过搜索引擎能搜到该网站的词汇.最好控制在10个以内. 基本语法: <meta name="keywords" content="具 ...

  10. wireshark相关知识

    wireshark抓包原理如下 https://www.cnblogs.com/yhcreak/p/5911904.html