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

# 前提
service workerを最近触るようになったので、その備忘録も兼ねて投稿します。
|使用環境|バージョン|備考|
|:--|:--|:--|
|Laravel|9|本題とあんまり関係ない|
|Service Worker||重要|


# 事象
service workerを実装して確認でいろんなページを開いてたらエラー発生
具体的には、
1. ログイン
1. ホーム画面でservice workerが登録完了
1. ログアウト
1. ログイン画面に戻った状態でURLにホーム画面に飛ぼうとするとエラー

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

https://developer.chrome.com/docs/workbox/caching-strategies-overview/

https://blog.formpl.us/how-to-handle-post-put-requests-in-offline-applications-using-service-workers-indexedb-and-da7d0798a9ab


下記ファイルをpublic直下に置いとく。
```javascript: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を登録できるように、ホーム画面に書きを書いとく。
```php: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](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/895907/98340161-de54-7229-9d78-b3330401daf7.png)

# 詳細
本来の動きは下記の通り。
 - アクセス先 ... ホーム画面
 - リダイレクト先 ... ログイン画面
SWをデバッグしながら確認していくと、
|http|url|status|
|:--|:--|:--|
|request|ホーム画面|なし|
|response|ログイン画面|200|

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

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

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

# 原因

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

https://stackoverflow.com/questions/45434470/only-in-chrome-service-worker-a-redirected-response-was-used-for-a-reque

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

```javascript: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を使おう。