Rails + Nuxt.js (SSR) でデータが壊れる原因と対策
はじめに
Rails 側で生成した URL のパラメータが Nuxt.js を経由するとまれにおかしくなる現象に二年以上悩まされており、一次凌ぎな対応を繰り返していたら収拾がつかなくなってきた。そこで根本的な原因を調査した。
データが壊れる過程
| 状態 | 整合性 | |
|---|---|---|
| 元の文字列 | a b+ | ○ |
| Rails側でエンコード[1] | a+b%2B[2] | ○ |
| Nuxt.js 側[3]で受け取ってデコード | a+b+[4] | × |
| Nuxt.js側からRails側に投げる[5] | a%2Bb%2B | × |
| Rails側で受けてデコード | a+b+ | × |
Nuxt.js 側で + をスペースに戻さないまま %2B を + に戻しているため、元がスペースだったのか + だったのかわからなくなっている。
対策1. スペースを含めない
| 状態 | 整合性 | |
|---|---|---|
| 元の文字列 | a b+ | ○ |
| スペースをドットに置換 | a.b+ | ○ |
| Rails側でエンコード | a.b%2B | ○ |
| Nuxt.js 側で受け取ってデコード | a.b+ | ○ |
| Nuxt.js側からRails側に投げる | a.b%2B | ○ |
| Rails側で受けてデコード | a.b+ | ○ |
| ドットをスペースに置換 | a b+ | ○ |
スペースを含まないことで問題をギリ回避する。この方法はあまりにも消極的でかつ根本的な解決にもなっていない。
対策2. エンコードデコードの世界から脱却
| 状態 | 整合性 | |
|---|---|---|
| 元の文字列 | a b+ | ○ |
| URL Safe Base64 形式に変換 | YSBiKw | ○ |
| Rails側でエンコード | YSBiKw | ○ |
| Nuxt.js 側で受け取ってデコード | YSBiKw | ○ |
| Nuxt.js側からRails側に投げる | YSBiKw | ○ |
| Rails側で受けてエンコード | YSBiKw | ○ |
| URL Safe Base64 形式から復元 | a b+ | ○ |
中途半端に置換して戻すぐらいなら一切エンコード・デコードと関わらないところまで持っていく。
対策3. Rails側でエンコードしない(没)
そうすると下のようにURLがスペースで分離してしまうため問題をさらに複雑にしてしまう。
ブラウザが仮に解釈してくれたとしても Nuxt.js 側は a+b+ と解釈してデータは壊れる。
対策4. Nuxt.js を更新する
試しに 2.14.12 から 2.15.8 に更新したら直った[6]。Rails 側で回避策を考えるのではなく問題が起きている方をどうにかするべきだった。つまり対策1も2もいらない。
反省
フレームワークの更新に追随していかないと本来やる必要のなかった調査と無駄な対策に時間を食うことになる。
-
エンコード用のメソッドは URI.encode_www_form_component ↩︎
-
もちろんRails側で受ければ元に戻る
URI.decode_www_form_component("a+b%2B")→a b+↩︎ -
Nuxt.js のサーバーサイドの asyncData() の中の
context.queryだけがおかしい クライアントサイドのthis.$route.queryは問題ない。 ↩︎ -
unescape または decodeURIComponent を使うとそうなる。decodeURI では何も変化しない。 こちらのサイトが確認時に便利 https://www.online-toolz.com/tools/javascript-escape-functions.php ↩︎
-
取得したデータを引数とし、axios経由でRails側のAPIをGETする ↩︎
-
一方で 2.14.12 では何の問題もなかったE2Eテストが 2.15.8 では落ちまくる ↩︎
Discussion