実験環境は Chrome Canary 43.0.2314.0 だけど動かしてる分は現時点の安定版(40)でも動くはず
やりたいこと
http://localhost:3000/api/*
へのリクエストを全部serviceworkerに任せたい。
先行実装として Jxck/response-injection がある。
やり方
sw.js(になるsw.coffee)
console.log 'worker started'
self.onfetch = (event) ->
console.log 'onfetch', event.request.url
if event.request.url.indexOf(location.origin+'/api') > -1
event.respondWith new Response '{"foo": "bar"}',
status: 200
headers:
'Content-Type': 'application/json'
今回は location.origin+'/api', つまり http://localhost:3000/api 以下ならJSONを返却する。それ以外はスルーしてくれる(スルーする為の正しい定義がよくわからん。respondWithしなかったらデフォルトの挙動にフォールバックした)
ServiceWorkerのよくあるサンプルならCacheAPIを使ってアセットをキャッシュするんだけど、今回はどうでもいい。
browserで実行する側
window.addEventListener 'load', =>
navigator.serviceWorker.register 'sw.js', scope: '.'
.then (r) ->
console.log('service-worker registered')
xhr = new XMLHttpRequest()
xhr.open 'GET', 'api/foo'
xhr.responseType = 'json'
xhr.addEventListener 'load', (data) ->
console.log 'loaded', data.target.response
xhr.send()
.catch (err) ->
console.error('fail to register', err)
(どうも一回は失敗する。二回目以降有効? navigator.serviceWorker.ready
が promiseなのでthenを仕込んでみたけどダメだった。誰か解決策教えてください)
余計な要素が介入してこないように生のxhrでやった。(ここでfetch APIがあったらなぁ)
ハマった点
最初、scope: 'api/'
と書いてたら / からのアクセスがonfetchに飛ばず、URL直接踏むとJSON帰ってくる、みたいな挙動で悩んでいたんだけど、次の記事をみて気づいた
Service worker が拓く mobile web の新しいかたち
serviceworkerは自分自身の属するドメインしか扱えないので、 / から /api/ のservice-workerを登録することはできても(なのでURL直接踏むと動く)、 / からの xhr は /api/ 以下に登録された service-workerに対して働かない!!
ということで3時間ぐらい悩みました。Twitterで質問に答えてくれた、ChromiumでServiceWorker実装している @nhiroki_
さんありがとうございます…