iOS(アプリ)+ajaxでDataCloneError
Webやアンドロイド(アプリ)では問題なかったけどiOS(アプリ)のみで「DataCloneError: The object could not be cloned.」が出てくる
作成日:2020-05-18, 更新日:2020-05-21
元ソースと対応
・HTML側にformタグは無い(※Javascriptで送りたいデータを準備する)
元 | 対応後 |
---|---|
// 送りたいデータを都度、作成 var fd = new FormData(); fd.append('hoge01', xx1); fd.append('hoge02', xx2); fd.append('hoge03', xx3); $.ajax({ url: xxxxxxxxxxxx, data: fd, type: 'POST', dataType: 'json', contentType: false, processData: false, }).done(function(data) { ~以下、略~ |
// 送りたいデータを都度、作成 $('#ID_xxxx').remove(); var _ipt = { hoge01: xx1, hoge02: xx2, hoge03: xx3 }; var _form = document.createElement("form"); for (var i in _ipt) { var _tmpInput = document.createElement("input"); _tmpInput.setAttribute("name", i); _tmpInput.setAttribute("type", "text"); _tmpInput.setAttribute("value", _ipt[i]); _form.appendChild(_tmpInput); } _form.id = "ID_xxxx"; _form.style = "overflow:hidden;width:0;height:0;"; document.body.appendChild(_form); var fd = $('#ID_xxxx').serialize(); $.ajax({ url: xxxxxxxxxxxx, data: fd, type: 'POST', dataType: 'json', contentType: 'application/x-www-form-urlencoded; charset=UTF-8', processData: false, }).done(function(data) { ~以下、略~ ※serialize()を使うためにわざわざformタグを作っている |
・元データは、「FormData()」を「contentType:false」で送る
・変更後は、「serialize()」したデータを「contentType:'application/x-www-form-urlencoded; charset=UTF-8'」で送る
めも
- 「FormData()」が使えない(IE8、IE9以外にも最新のiOSアプリだと使えないってコトかな?)
- 「serialize()」+「POST」で送るなら「contentType:'application/x-www-form-urlencoded; charset=UTF-8'」にする必要があるっぽい
※「FormData()」のときは「contentType:false」
serialize()
「serialize()」はformタグの中身を「hoge01=xx1&hoge02=xx2&hoge03=xx3」という文字列に変換してくれる関数。
※対象は、name属性があるヤツだけ
エスケープ処理をしてやるならわざわざ「serialize()」を使わなくてもいいはず。
経緯
・もともとWEBがあって、ajaxを使ってゴニョゴニョしていた
・そのWEBをアプリで表示するようにした(他人が作業:詳細不明)
・アプリのバージョンアップに伴いiOS(アプリ)でエラーになる
→「修正が必要です」と言われた
状況
・PCでWEBを確認→問題なし
・アンドロイドからWEBを確認→問題なし
・アンドロイド(アプリ)で確認→問題なし
・iOSからWEBを確認→問題なし
・iOS(アプリ)で確認→エラー
サーバ側(PHP)
サーバ側(PHP)・・・エラー以前にそもそもアクセスされていない
・・・ajax(Javascript)側でエラーがあると推測
ajax(Javascript)側の問題
iOS(アプリ)のみ値がクリア or 別の内容に書き換えられている・・・等
リクエストURLの問題
まず、ajaxで使うリクエストURLの問題
→URLがクリアされている or 書換えられている等の問題は無し
フォームデータの問題
「$(xxx).〇〇〇」みたいなトコで取得失敗している可能性
→どこもエラーになっていないっぽい
ajax自体の問題
▼こんなエラーが出ていた
DataCloneError: The object could not be cloned.
渡す値がおかしいってコトなんだろうけど・・・調べると「new FormData()」ってトコがダメと言う人たちが多いっぽい
※ただ、古い情報ばっかり・・・
調査
「new FormData()」がダメってコトで別の方法を調べた。
※色々調べながらしていたので、そもそもダメって内容も含まれる
▼元はこんな感じ
var fd = new FormData(); fd.append('hoge', 'xxx'); fd.append('foo', 'xxx'); fd.append('bar', 'xxx'); $.ajax({ url: xxxxxxxxxxxx, data: fd, type: 'POST', dataType: 'json', contentType: false, processData: false, ~ 以下、略 ~ });
オブジェクトでそのまま送る
var fd = { hoge: 'xxx', foo: 'xxx', bar: 'xxx' };
→サーバ側にデータが送られていない
オブジェクトをserialize()化
var ipt = { hoge: 'xxx', foo: 'xxx', bar: 'xxx' }; var fd = ipt.serialize();
→formじゃないとダメ。
serialize()の結果を送る
serialize()の結果って「hoge01=xx1&hoge02=xx2&hoge03=xx3」のはずだから、直接、セット。
var fd = 'hoge01=xx1&hoge02=xx2&hoge03=xx3';
→サーバ側にデータが送られていない
serialize()してから送る
面倒だけど、いったんformを作成してやる。その後、作成したformをserialize()・・・という流れ
var _ipt = { hoge01: xx1, hoge02: xx2, hoge03: xx3 }; var _form = document.createElement("form"); for (var i in _ipt) { var _tmpInput = document.createElement("input"); _tmpInput.setAttribute("name", i); _tmpInput.setAttribute("type", "text"); _tmpInput.setAttribute("value", _ipt[i]); _form.appendChild(_tmpInput); } _form.id = "ID_xxxx"; _form.style = "overflow:hidden;width:0;height:0;"; document.body.appendChild(_form); var fd = $('#ID_xxxx').serialize();
→サーバ側にデータが送られていない
GET送信
GETで送る気は無いが、GETで送れるか確認だけしておく。
$.ajax({ ~ 略 ~ type: 'GET', ~ 以下、略 ~ });
→サーバ側にデータは送られる
serialize()してJSONでおくる
var fd = $('#ID_xxxx').serialize(); $.ajax({ ~ 略 ~ data: JSON.stringify(fd), type: 'POST', ~ 以下、略 ~ });
→サーバ側にデータが送られていない
・・・そもそもJSONにする必要が無いから・・・
serialize()+contentTypeの設定
「contentType:false」が気になったので変更してみた。
var _ipt = { hoge01: xx1, hoge02: xx2, hoge03: xx3 }; var _form = document.createElement("form"); for (var i in _ipt) { var _tmpInput = document.createElement("input"); _tmpInput.setAttribute("name", i); _tmpInput.setAttribute("type", "text"); _tmpInput.setAttribute("value", _ipt[i]); _form.appendChild(_tmpInput); } _form.id = "ID_xxxx"; _form.style = "overflow:hidden;width:0;height:0;"; document.body.appendChild(_form); var fd = $('#ID_xxxx').serialize(); $.ajax({ ~ 略 ~ contentType: 'application/x-www-form-urlencoded; charset=UTF-8', ~ 以下、略 ~ });
→データが送れた
値を直で指定
試してはいないけど、下記のような感じでもいけるはず
var fd = 'hoge01=xx1&hoge02=xx2&hoge03=xx3'; $.ajax({ ~ 略 ~ contentType: 'application/x-www-form-urlencoded; charset=UTF-8', ~ 以下、略 ~ });
この場合、エスケープ処理をしっかりしてあげる必要がある
→面倒そうなので、未調査
追記
2018年あたりの情報で「iOS+FormData()」で似たようなエラーが出ていたらしい。
この時の原因は、formに「input type=file」があって「値が空」のときに出るiOSのSafariのバグだそうだ。
→「値が空のとき、FormData()に含めない」という感じにしてやれば解決したそうだ。
私の場合は・・・・ファイルは無い。
念のために「value」が空の要素を「FormData()」に含めないようにしてみたが、エラーだった。
2020-05-21
useragentを確認してみた。
・「Win+Chrome」 - Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36
・「iOS+ブラウザ」 - Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.1.2 Mobile/15E148 Safari/604.1
・「iOS+アプリ」 - Mozilla/5.0 (iPhone; CPU iPhone OS 12_4_6 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) XXXXXXXXXX (iPhone7,2; iOS 12.4.6)
「XXXXXXXXXX」としているトコはカスタムのuseragent。
とりあえずSafariは関係ないっぽい。
・・・ってことは、iOSの問題 or アプリを作ったヤツが問題・・・どっちなんだろう?