问题
I need to make a POST request and send some data. I'm using the service worker sync
to handle offline situation.
But is there a way to pass the POST data to the service worker, so it makes the same request again?
Cause apparently the current solution is to store requests in some client side storage and after client gets connection - get the requests info from the storage and then send them.
Any more elegant way?
PS: I thought about just making the service worker send message to the application code so it does the request again ... but unfortunately it doesn't know the exact client that registered the service worker :(
回答1:
You can use fetch-sync
or i use postmessage to fix this problem, which i agree that indexedDB looks trouble.
first of all, i send the message from html.
// send message to serviceWorker function sync (url, options) { navigator.serviceWorker.controller.postMessage({type: 'sync', url, options}) }
i got this message in serviceworker, and then i store it.
const syncStore = {} self.addEventListener('message', event => { if(event.data.type === 'sync') { // get a unique id to save the data const id = uuid() syncStore[id] = event.data // register a sync and pass the id as tag for it to get the data self.registration.sync.register(id) } console.log(event.data) })
in the sync event, i got the data and fetch
self.addEventListener('sync', event => { // get the data by tag const {url, options} = syncStore[event.tag] event.waitUntil(fetch(url, options)) })
it works well in my test, what's more you can delete the memory store after the fetch
what's more, you may want to send back the result to the page. i will do this in the same way by postmessage.
as now i have to communicate between each other, i will change the fucnction sync into this way
// use messagechannel to communicate sendMessageToSw (msg) { return new Promise((resolve, reject) => { // Create a Message Channel const msg_chan = new MessageChannel()
}) } // send message to serviceWorker // you can see that i add a parse argument // this is use to tell the serviceworker how to parse our data function sync (url, options, parse) { return sendMessageToSw({type: 'sync', url, options, parse}) }// Handler for recieving message reply from service worker msg_chan.port1.onmessage = event => { if(event.data.error) { reject(event.data.error) } else { resolve(event.data) } } navigator.serviceWorker.controller.postMessage(msg, [msg_chan.port2])
i also have to change the message event, so that i can pass the port to sync event
self.addEventListener('message', event => { if(isObject(event.data)) { if(event.data.type === 'sync') { // in this way, you can decide your tag const id = event.data.id || uuid() // pass the port into the memory stor syncStore[id] = Object.assign({port: event.ports[0]}, event.data) self.registration.sync.register(id) } } })
up to now, we can handle the sync event
self.addEventListener('sync', event => { const {url, options, port, parse} = syncStore[event.tag] || {} // delete the memory delete syncStore[event.tag] event.waitUntil(fetch(url, options) .then(response => { // clone response because it will fail to parse if it parse again const copy = response.clone() if(response.ok) { // parse it as you like copy[parse]() .then(data => { // when success postmessage back port.postMessage(data) }) } else { port.postMessage({error: response.status}) } }) .catch(error => { port.postMessage({error: error.message}) }) ) })
At the end. you cannot use postmessage to send response directly.Because it's illegal.So you need to parse it, such as text, json, blob, etc. i think that's enough.
As you have mention that, you may want to open the window. i advice that you can use serviceworker to send a notification.
self.addEventListener('push', function (event) {
const title = 'i am a fucking test'
const options = {
body: 'Yay it works.',
}
event.waitUntil(self.registration.showNotification(title, options))
})
self.addEventListener('notificationclick', function (event) {
event.notification.close()
event.waitUntil(
clients.openWindow('https://yoursite.com')
)
})
when the client click we can open the window.
回答2:
To comunicate with the serviceworker I use a trick:
in the fetch eventlistener I put this:
self.addEventListener('fetch', event => {
if (event.request.url.includes("sw_messages.js")) {
var zib = "some data";
event.respondWith(new Response("window.msg=" + JSON.stringify(zib) + ";", {
headers: {
'Content-Type': 'application/javascript'
}
}));
}
return;
});
then, in the main html I just add:
<script src="sw_messages.js"></script>
as the page loads, global variable msg will contain (in this example) "some data".
来源:https://stackoverflow.com/questions/41798009/pass-custom-data-to-service-worker-sync