JavaScript
HTML5
ServiceWorker

ServiceWorkerでモックサーバーを作る

More than 3 years have passed since last update.

実験環境は 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_ さんありがとうございます…