[Javascript] Ajaxライブラリ

2016年7月30日

Javascript テクノロジー プログラミング

「jQueryを使えばいいじゃない」とよく言われるが、たかが外部サーバーからデータを取得するだけの事を自分で行えないのが嫌だったので、以前から自作したライブラリを使っていたが、この度、不具合を発見したので、修正版を公開しておきます。

過去の記事

WEBページ内で外部サーバーに設置してあるTEXTファイルを読んだり、書いたりするために、URLに対してデータをリクエストして、返り値を取得した後 イベント処理として関数を実行できるライブラリを作っていた。 [プログラム学習] JavaScript #4 「ファイル読み込み」 [プログラム学習] JavaScript #5 「ファイル書き込み」

改修版

(function(){ var $$ = {}; /** * Ajax */ $$.ajax = { createHttpRequest:function(){ //Win ie用 if(window.ActiveXObject){ try { //MSXML2以降用; return new ActiveXObject("Msxml2.XMLHTTP") } catch(e){ try { //旧MSXML用; return new ActiveXObject("Microsoft.XMLHTTP") } catch(e2){ return null } } } //Win ie以外のXMLHttpRequestオブジェクト実装ブラウザ用; else if(window.XMLHttpRequest){ return new XMLHttpRequest() } else{ return null } }, /** * XMLHttpRequestオブジェクト生成 */ set:function( option ){ if(!option){return} var httpoj = new $$.ajax.createHttpRequest(); if(!httpoj){return} //open メソッド; httpoj.open( option.method , option.url , option.async ); httpoj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); //受信時に起動するイベント; httpoj.onreadystatechange = function(){ //readyState値は4で受信完了; if (httpoj.readyState==4){ //コールバック option.onSuccess(httpoj.responseText); } }; //query整形 var data = []; if(typeof(option.query)!="undefined"){ for(var i in option.query){ data.push(i+"="+encodeURIComponent(option.query[i])); } } if(typeof(option.querys)!="undefined"){ for(var i=0;i<option.querys.length;i++){ data.push(option.querys[i][0]+"="+encodeURIComponent(option.querys[i][1])); } } //send メソッド if(data.length){ httpoj.send( data.join("&") ); } else{ httpoj.send(); } }, //コールバック関数 ( 受信時に実行されます ); onSuccess:function(oj){ //レスポンスを取得; var res = oj.responseText; //console.log(res); //ダイアログで表示; if(res && res.match(/^[a-z|$]/)){ eval(res); } } }; //path-info Ex):p=location.href $$.pathinfo = function(p){ var basename="", dirname=[], filename=[], ext=""; var p2 = p.split("?"); var urls = p2[0].split("/"); for(var i=0; i<urls.length-1; i++){ dirname.push(urls[i]); } basename = urls[urls.length-1]; var basenames = basename.split("."); for(var i=0;i<basenames.length-1;i++){ filename.push(basenames[i]); } ext = basenames[basenames.length-1]; return { "hostname":urls[2], "basename":basename, "dirname":dirname.join("/"), "filename":filename.join("."), "extension":ext, "query":(p2[1])?p2[1]:"", "path":p2[0] }; }; $$.urlinfo=function(uri){ if(!uri){uri = location.href} var data={}; //URLとクエリ分離分解; var query=[]; if(uri.indexOf("?")!=-1){query = uri.split("?")} else if(uri.indexOf(";")!=-1){query = uri.split(";")} else{ query[0] = uri; query[1] = ''; } //基本情報取得; var sp = query[0].split("/"); var data={ url:query[0], dir:this.pathinfo(uri).dirname, domain:sp[2], protocol:sp[0].replace(":",""), query:(query[1])?(function(q){ var data=[]; var sp = q.split("&"); for(var i=0;i<sp.length;i++){ var kv = sp[i].split("="); if(!kv[0]){continue} data[kv[0]]=kv[1]; } return data; })(query[1]):[], }; return data; }; /** * 直上の対象エレメントを取得 * param @ elm : target-element * param @ type : [class , id] * param @ value : string **/ $$.getElementByParentNode = function(elm,type,value){ if(!elm){return null} if(type === "class"){ if(elm.parentNode.className.indexOf(value)!=-1){ return elm.parentNode; } else{ return $$.getElementByParentNode(elm.parentNode,type,value); } } else if(type === "id"){ if(elm.parentNode.id && elm.parentNode.id === value){ return elm.parentNode; } else{ return $$.getElementByParentNode(elm.parentNode,type,value); } } }; window.$$LIB = $$; return $$; })();

解説

今回発見した問題点というのは、このAJAX処理を同一ページ内で連続して実行した時に、ライブラリ内で使っている変数が、後から実行した処理が上書きしてしまうという不具合を見つけた。 例えば、複数のリスト型エレメントがあった場合、そのリスト1つずつにサーバーに問い合わせてそれぞれ別の値を返す処理を入れた時に、通常のfor分内でこのajax処理を行なってしまうと、楽勝で結果がバグってしまうのである。 このバグを修正するのは、ぶつかっている変数の「httpoj」の部分をグローバル変数になってしまっているので、これをローカル変数にして、関数が呼ばれる度に完結させなければいけない。 そして、「createHttpRequest」関数をnew演算子を使用して、これにより関数を呼び出す度にインスタンス化されるので、結果グローバルから上書きされることがなくなるのだ。 以前のバージョンと変えたのは、set関数の中身だけで、使い方は互換性をもたせているので、ライブラリを更新するだけでバグが無くなるハズ。

使い方

ajax.set({ url:"http://hoge.com/***.php", //対象URL methos:"post", //POST or GET async:true, //true or false query:{"data":"abcdefg"}, //queryをjson記述で書ける。 onSuccess:function(res){console.log(res)} }); 上記をサンプルとして、適宜内容を変更することでサーバー通信が可能になる。 今回のライブラリ改修で、連続アクセスも可能になるので、より強固なライブラリになったという事だ。 なんか、バグが治って、少しうれしい・・・

追記 : 送信Query仕様

POST送信を行う場合にQuery情報の登録方法は2通りの仕様を採用しています。

1. 配列方式

Ex) querys:[ “aa[]=1” , “aa[]=2” , “aa[]=3” ] この方法のメリットは、PHPサーバーへのクエリ送信を行う場合にExの様に同じkey名をセットする事ができます。

2. オブジェクト方式

Ex) quert:{ “aa”:1 , “bb”:2 , “cc”:3 } スタンダードな方法なので、通常はこちらを使うほうが分かりやすいかもしれません。

注意点

クエリ設定を行なってmethodを「GET」にすると、ライブラリ内で自動的にURLにクエリを追加する方式になりますが、古いブラウザなどは数KbのURLしか対応していないものもあるので、長文送信の場合はPOSTで行いましょう。 また、urlの箇所にGET記述を行う場合は、POST設定を行なってもurl部分はGET記述で送信されます。 これはサーバーセキュリティでPOSTとGETを切り分けている場合を想定しているので、ライブラリでの対応範囲を広げるための処置なので、ご自身で切り分けてご使用ください。

ajax処理で不具合を見つけたので改修をしました。

issue

「content-type」が「application/x-www-form-urlencoded」に固定されている。

dealing with

optionに「type」を追加して、「content-type」を個別にセットできるようにした。 ※デフォルト(指定なし)は「application/x-www-form-urlencoded」

source

$$.ajax = { createHttpRequest:function(){ //Win ie用 if(w.ActiveXObject){ //MSXML2以降用; try{return new ActiveXObject("Msxml2.XMLHTTP")} catch(e){ //旧MSXML用; try{return new ActiveXObject("Microsoft.XMLHTTP")} catch(e2){return null} } } //Win ie以外のXMLHttpRequestオブジェクト実装ブラウザ用; else if(w.XMLHttpRequest){return new XMLHttpRequest()} else{return null} }, /** * XMLHttpRequestオブジェクト生成 */ set:function(option){ if(!option){return} var httpoj = new $$.ajax.createHttpRequest(); if(!httpoj){return;} //open メソッド; httpoj.open( option.method , option.url , option.async ); if(typeof option.type != "undefined"){ httpoj.setRequestHeader('Content-Type', option.type); } else{ httpoj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); } //受信時に起動するイベント; httpoj.onreadystatechange = function(){ //readyState値は4で受信完了; if (httpoj.readyState==4){ //コールバック option.onSuccess(httpoj.responseText); } }; //query整形 var data = []; if(typeof(option.query)!="undefined"){ for(var i in option.query){ data.push(i+"="+encodeURIComponent(option.query[i])); } } if(typeof(option.querys)!="undefined"){ for(var i=0;i<option .querys.length;i++){ data.push(option.querys[i][0]+"="+encodeURIComponent(option.querys[i][1])); } } //send メソッド if(data.length){ httpoj.send( data.join("&") ); } else{ httpoj.send(); } }, //コールバック関数 ( 受信時に実行されます ); onSuccess:function(res){console.log(res)} }; $$.ajax.set({ "url" :scriptPath + "service/protech/js/common.js", "method":"get", "async" :"true", "type" :"text/plain", "onSuccess":function(res){ eval(res); } });

注意点

type指定する時は、methodを「get」にしてください。