HTMLをどこまで便利に使いこなせるか研究所所長の、ユゲタです。
あ、この研究所、今この瞬間に僕が立ち上げたモノで、研究員は僕一人しかいません。
そして、何よりHTMLを便利に使いこなせることで、これまでサーバーに頼っていた内容も、
ローカル環境だけで処理することができ、まさにエコなITが実現できるという未来像です。
それを実現してくれるのが、ブラウザサイドJavascriptを使いこなすことにあるとも、言って良いんですけどね。
とある、システムを作っている時に、コンバージョンを含む入力フォームを複数ページ用意して、ページ遷移をさせながら
EFO実装を行っていたですが、HTMLからHTMLにデータをsubmitした時に、javascriptでそのリクエストヘッダまたは、レスポンスヘッダデータからPOST情報を取得することができないものか?
という事を考えたのがきっかけで、結果はとあるライブラリを自作して実現できたので、同じような事を探している人がネットで何人もいたので、
そのプログラムもブログで公開しておきます。
HTMLでPOST-GETは、できない!!
はい、これ、海外も含めたネット上で、
「javascript post 受信」と検索して、たくさんの結果がでますが、
どのサイトにも、「できません!」と言い切られています。
ブラウザのコンソール画面で見ると、POSTデータが見えていて、どうにももどかしいんですが、基本的にできないことを実現させると、なんとも気持ちがいいので、
せめて特定の環境でできないものか、考えてみました。
まず、どういう風にできないかを納得してみたいので、それを解説してみると、
javascriptでヘッダ情報にアクセスする命令は、いくつかあるんですが、この場合レスポンスヘッダにアクセスするのが一般的だろうと調べても、
ajax送信をしたあとでのヘッダ情報を取得する命令ばかりしか、ネット検索では情報を得られることができませんでした。
そして、javascriptに
responce()という命令があったので、調べてみましたが、
safariで一分機能が使えないという、もしかしたらiPhoneで使えないかもしれない事象がわかったので、これは使わない方向にしました。(今回は・・・)
もっと根本的な実装を考えてみた
できない技術を追求するより、それに該当する振る舞いができればそれでいいじゃないか。
という持論の元、localStorageを瞬間利用して受け渡しをする方式を考えてみました。
次のような仕様です。
# send処理
formタグからsubmitイベントを取得して、submit(post)する情報を全て一旦localStorageに保持します。
# get処理
postされた情報はlocalStorageに格納されているので、遷移した先で受信と称してlocalStorageからデータを受信すれば、完了という感じです。
# セキュリティの考慮
基本的にlocalStorageを利用するので、同一ドメインでの利用が必須です。
そして、受信したらlocalStorageのデータは即座に消して何事もなかったかのようにする。
念の為に、5秒ルールを設けておいて、遷移に5秒以上かかった場合は、データを利用せずに捨てるようにすることで、
残ってしまった別フォームのデータを参照してしまうことを防ぎます。
ソースコード
(function(){
/*
* Form Trans
* ==
* # Author
* - Yugeta.Koji
* - MYNT.Inc,
* - 2021.07.19
*
* # Summary
* - ページ内のformタグ内のデータ送信を html -> html で実現させる。
* - サーバーを介さずに値をページ移行できるので、セキュリティに最適
*
* # Speficication
* - localStorageを使って、実現。
* - 送信時(セットしたformタグのonsubmitタイミング)にlocalStorageに暗号化して保存
* - 受信時(起動処理)にlocalStorageから取得して復元して、localStorageデータを削除する。
* - 最終的にサーバーにPOSTする。
* - 別ページとのコンフリクトを無くすため、データの有効期限を5秒とする。
*/
let __options = {
ls_key : "mynt_form_transition", // localStorage保存用key値
expire : 5000 // 有効期限(ms)
};
function MAIN(){
// post
if(!form_transition || !form_transition.form){return;}
let target_form = document.querySelector(form_transition.form);
if(!target_form){return;}
this.form = target_form;
target_form.addEventListener("submit" , this.form_post.bind(this));
// get
this.post_get();
};
// localStorageに保存
MAIN.prototype.form_post = function(e){
if(!this.form || !this.form.elements){return;}
let inputs = [];
for(let elm of this.form.elements){
switch(elm.type){
case "radio":
case "checkbox":
if(elm.checked === true){
inputs.push({
type : elm.type,
name : elm.name,
value : elm.value
});
}
break;
case "textarea":
case "select":
case "text":
default:
let val = elm.value;
inputs.push({
type : elm.type,
name : elm.name,
value : val
});
break;
}
}
let data = {
expire : (+new Date()),
inputs : inputs
};
localStorage.setItem(__options.ls_key , JSON.stringify(data));
};
// localStorageからデータを取得して、hidden項目として、自動作成する。
MAIN.prototype.post_get = function(){
if(!this.form){return;}
let json = localStorage.getItem(__options.ls_key);
if(!json){return;}
localStorage.removeItem(__options.ls_key);
let data = JSON.parse(json);
if(!data.expire || ((+new Date()) - data.expire) > __options.expire){return;}
this.make_input(data.inputs);
};
MAIN.prototype.make_input = function(inputs){
if(!inputs || !inputs.length){return;}
let new_inputs = [];
// 同一name値の除外
for(let input of inputs){
if(!input.name){continue;}
if(this.form[input.name]){continue;}
new_inputs.push(input);
}
if(!new_inputs.length){return;}
for(let input of new_inputs){
let elm = document.createElement("input");
elm.type = "hidden";
elm.name = input.name;
elm.value = input.value;
this.form.appendChild(elm);
}
};
// new MAIN();
switch(document.readyState){
case "complete" : new MAIN();break;
default : window.addEventListener("load" , function(){new MAIN()});break;
}
})()
実装方法
入力フォームとその遷移後のページにライブラリタグを貼り付けます。
※この場合、ページのどの位置にタグを記載しても動作するようにしています。
<script src="form_transition.js"></script>
<script>
form_transition = {
form : "form[name='form1']"
};
</script>
そして、上記のように同じ位置に書かなくてもいいのですが、ページ内のどこかに、scriptタグで、form_transitionというグローバルobjectに、formタグのセレクタを書き込んでおきます。
※CVページなどのように受け側でformがない場合は、このグローバルobjectはセットしなくても大丈夫です。
これだけで、対象のフォーム内に、前のページからの入力フォームのname値とその値が、自動的にhidden情報として作られます。
実際にcvページの前にphpなど記述して、サーバーにデータを送る処理を入れることで、本番環境でも利用することができるでしょう。
作ってみて思ったこと
これ実際にやってみたところ、postデータをわざわざサーバーに送らずに済むので、データハッキングされることもなければ、データの容量などを気にする必要もなくなるという
良いことだらけの処理だということに気が付きました。
このプログラムでは、jsonデータとして、平文で保存していますが、これを暗号化などをすることで、よりセキュアな状態に持っていけると思います。
あと、実際にページを開くまで5秒以上かかるサイトなんてのもあることを考えると、この時間制限ももう少し検討の余地があるかもしれませんが、
とりあえず、ボクの開発環境では、便利に問題なく使えています。
さて、responce機能事態をもう少し掘り下げて研究してみますか、
また、別の方法でツールを作ったら、紹介したいと思います。
でわでわ。
0 件のコメント:
コメントを投稿