许多非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. TFS2015安装、配置

    一.安装,参考:http://www.cnblogs.com/WilsonWu/archive/2011/11/24/2261674.html 二.配置,参考:http://www.cnblogs.c ...

  2. Linux环境下nginx集群搭建

    #确保安装nginx,stream模块默认不安装的,需要手动添加参数:–with-stream, nginx1.9或以上版本 #nginx.conf文件中,添加以下内容(只供参考),这个不能放在htt ...

  3. java clone()

    Java中对象的创建   clone顾名思义就是复制, 在Java语言中, clone方法被对象调用,所以会复制对象.所谓的复制对象,首先要分配一个和源对象同样大小的空间,在这个空间中创建一个新的对象 ...

  4. 信号报告-java

    无线电台的RS制信号报告是由三两个部分组成的: R(Readability) 信号可辨度即清晰度. S(Strength) 信号强度即大小. 其中R位于报告第一位,共分5级,用1-5数字表示. 1-- ...

  5. Unity对象池的实现

    对象池是一个单例类: using System.Collections; using System.Collections.Generic; using UnityEngine; public cla ...

  6. python 8

    一.文件操作初识 1. path 文件路径 F:\文件.txt encoding 编码方式 utf-8, gbk ... mode 操作方式 只读,只写,读写,写读,追加... f1 = open(r ...

  7. 我的代码-unsupervised learning

    # coding: utf-8 # In[1]: import pandas as pdimport numpy as npfrom sklearn import treefrom sklearn.s ...

  8. ubuntu下cannot find lib....so.x 寻找动态链接库

    默认从/lib . /usr/lib 以及配置文件/etc/ld.so.conf内所列的目录下加载.so文件, 进而创建出动态装入程序(ld.so)所需的连接和缓存文件. 缓存文件默认为/etc/ld ...

  9. Centos7快速安装docker

    偶然间发现,docker的安装好容易啊 系统环境:centos7.3 yum源: docker:https://mirrors.aliyun.com/docker-ce/linux/centos/do ...

  10. jQuery汇总

    closest() 方法返回被选元素的第一个祖先元素. $("span").closest("ul")返回 <span> 的第一个祖先元素,是一个 ...