0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Service Workerでリダイレクトしたらエラーになった

Last updated at Posted at 2022-11-01

前提

service workerを最近触るようになったので、その備忘録も兼ねて投稿します。

使用環境 バージョン 備考
Laravel 9 本題とあんまり関係ない
Service Worker 重要

事象

service workerを実装して確認でいろんなページを開いてたらエラー発生
具体的には、

  1. ログイン
  2. ホーム画面でservice workerが登録完了
  3. ログアウト
  4. ログイン画面に戻った状態でURLにホーム画面に飛ぼうとするとエラー

コード

indexedDBを使ったPOSTメソッドを入れたかったので、下記のページを参考に作ってました。
かつ、Network Firstで実装。

下記ファイルをpublic直下に置いとく。

sw.js
const addResourcesToCache = async (resources) => {
    const cache = await caches.open("キャッシュテーブルの名前");
    await cache.addAll(resources);
  };
  
  self.addEventListener("install", (event) => {
    event.waitUntil(
      addResourcesToCache([
      ... 
      (キャッシュするファイル)
      ...
      ])
    );
  });

self.addEventListener('fetch', (event) => {
    // GETはネットワーク優先、ネットがなければキャッシュから取得
    if (event.request.clone().method === 'GET') {
        event.respondWith(caches.open(cacheName).then((cache) => {
        return fetch(event.request.url).then((fetchedResponse) => {
            cache.put(event.request, fetchedResponse.clone());
            return fetchedResponse;
        }).catch(() => {
            return cache.match(event.request.url);
        });
        }));
    // POSTはネットがなければindexedDBにpostMessageとかいろいろ。。。
    } else if (event.request.clone().method === 'POST') {
        event.respondWith(fetch(event.request.clone()).catch(function
        (error) {
            ...
            (POST処理の保存など)
            ...
        }))
    }
});

ログイン後のホーム画面でservice workerを登録できるように、ホーム画面に書きを書いとく。

home.blade.php
...
<script>
const registerServiceWorker = async () => {
    if ('serviceWorker' in navigator) {
        try {
            const registration = await navigator.serviceWorker.register(
            'sw.js', {scope: './',}
            );
            if (registration.installing) {
                console.log('Service worker installing');
            } else if (registration.waiting) {
                console.log('Service worker installed');
            } else if (registration.active) {
                console.log('Service worker active');
            }
        } catch (error) {
            console.error(`Registration failed with ${error}`);
        }
    }
};
</script>
...

Laravelではbreezeでかんたんなログイン機能を追加しておく。

そして本題。エラーを実際に起こす。
上記の通り、service workerが登録している状態でリダイレクトを起こすと、、、
スクリーンショット 2022-11-01 23.33.57.png

詳細

本来の動きは下記の通り。

  • アクセス先 ... ホーム画面
  • リダイレクト先 ... ログイン画面
    SWをデバッグしながら確認していくと、
    |http|url|status|
    |:--|:--|:--|
    |request|ホーム画面|なし|
    |response|ログイン画面|200|

そして、ネットワークを確認すると、

  1. ホーム画面 ステータス:302
  2. ログイン画面 ステータス:200

なので、ホーム画面の302でSWが悪さしているのか?と思ってた。

原因

そもそもセキュリティ上の問題で、service workerの機能でリダイレクトをしないようにしてるらしい。
リダイレクトの時はキャッシュしないで、そのままレスポンスを返せば問題ないとのこと。

解決策

event.response.redirectedがtrueになっているとエラーを返すらしい。
なので、request.redirect以外をそのまま返すリクエストを返す
新しくリクエストを作成してそのままレスポンスとして返す

sw.js

self.addEventListener('fetch', (event) => {
    // GETはネットワーク優先、ネットがなければキャッシュから取得
    if (event.request.clone().method === 'GET') {
        event.respondWith(caches.open(cacheName).then((cache) => {
        return fetch(event.request.url).then((fetchedResponse) => {
            // 追加
            if (fetchedResponse.redirected === true) {
                response = cleanResponse(fetchedResponse);
                return response;
            } else {
                cache.put(event.request, fetchedResponse.clone());
                return fetchedResponse;
            }
            // 追加終了
        }).catch(() => {
            return cache.match(event.request.url);
        });
        }));
    // POSTもネットワーク優先、ネットがなければindexedDBにpostMessageとかいろいろ。。。
    } else if (event.request.clone().method === 'POST') {
        event.respondWith(fetch(event.request.clone()).catch(function
        (error) {
            ...
            (POST処理の保存など)
            ...
        }))
    }
});

// 追加
function cleanResponse(response) {
    const clonedResponse = response.clone();

    const bodyPromise = 'body' in clonedResponse ?
    Promise.resolve(clonedResponse.body) :
    clonedResponse.blob();

    return bodyPromise.then((body) => {
        return new Response(body, {
            headers: clonedResponse.headers,
            status: clonedResponse.status,
            statusText: clonedResponse.statusText,
        });
    });
}

反省

解決策の

const bodyPromise = 'body' in clonedResponse ?
Promise.resolve(clonedResponse.body) :
clonedResponse.blob();
の書き方とか、
return new Response (body, {headers:...});

とか全然知らなかったので、そもそもjavascriptの学習足りないなとも思いました。
そしてswがトラウマになりそうです。
→Workboxを使おう。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?