nodejs学習の実現簡易ルート
18858 ワード
これまではデータ転送機能が実現されていましたが、ローカルサーバを構築するには簡単なルート機能が必要です.ローカルサーバで自分のテスト用に使うだけなので、あまり完璧なルート機能は必要ないです.だから、expressフレームを使わずに、自分で簡単なルートを実現して、自分の需要に応じてルート機能をカスタマイズすることができます.
ルート機能を作る前に、まずルート表を書きました.自分がたぶん実現したい四つのルートの転換効果を表しました.この四つの効果はまさに自分のプロジェクトに必要です.
第二種類:つまり、私が/indexを訪問する時、index.ページを出力します.
第三種類:もし私がtest?v=indexと入力したら、出力のページはmyindexとなります.両方の*は数値が同じです.
第四種類:静的資源の取得に用いられます.私はpublic/bi*/**を訪問すると、public下の任意のファイルを出力します.例えば、私の要求経路はpublic/biz 009/styleesheets/css/main.cssです.それでは、経路が変換されたファイルの経路はpublic/stylistheets/css/main.cssです.
この四つの効果を実現する目的を持って、自分の実現を始めました.
第一段コードは、mimesの内容が長いので、代わりに…を使います.
まず正則を書いて、正則は主に***と**を置き換えるために使われ、正則文字列に置き換えられます.
次にRouterの構造関数を実現し、入ってきたパラメータを簡易的に処理し、着信したパラメータは直接上のオブジェクトでもいいし、jsonファイルのパスでもいいです.構造関数はevalでオブジェクトに変換します.JSON.parseを使わないのは、jsonフォーマットに対する要求が厳しいので、書きにくいからです.
後にイベントクラスを引き継ぎ、外部からのイベントバインディングを起動しやすくします.
そして文字列を分割し、urlであれば対応するurl処理を行い、functionであれば保存方法を実行し、req,resに入る.
要求されたpathnameはやはり/public/biz 009/styleesheets/css/main.cssです.スキャン['/public/bi','B',''A',''B']
スキャンの最初のものが'/public/bi'である場合、public/biを正則に変換し、マッチングによって/public/biz 009/stylistheets/css/main.cssを:z 009/styless/css/mail.cssに変更します.
二つ目のBをスキャンします.Bのために上のALL_を使います.FILES_REG_STRは「\w._-」+をマッチングさせてB対応のZ 009を取得し、同時にパス/styless/css/main.cssに変更してスキャンします. 「'public'、'、'、''B'」は、AまたはBにスキャンしたとき、対応するBではなくAなので、スキャンインデックスを更新しないので、上はindex+++ではなく、直接breakで、次のステップを継続します.
3番目の''をスキャンしますので、スキャンを続けても大丈夫です.
スキャンの4番目はAで、同じです.styleesheets/css/を取得して、pathnameをmain.cssにします.この時にスキャンします.「'public'、'、''A'、'、'B'」はスキャンインデックスがAに留まっています.その結果、両方ともAです.
その後、スキャンを続けます.スキャンが終わるまでは、「public」、「A」、「B」を「public」、「styleesheets/css/'、''main.css'」に変えます.
最後にまたjoinが出てきた結果:public/styless heets/css/main.cssは変換された最終パス、つまりマッチングされたファイルパスです.
もう一つの方法はファイルの内容を出力することです.一つは404です.比較的簡単です.詳細には説明しません.
https://github.com/whxaxes/easy-router
ルート機能を作る前に、まずルート表を書きました.自分がたぶん実現したい四つのルートの転換効果を表しました.この四つの効果はまさに自分のプロジェクトに必要です.
{
"/my/**/*":"func:testFun",
"index":"url:index.html",
"test?v=*":"url:my*.html",
"/public/bi*/**/*":"url:public/**/*"
}
第一に、私の住所がmy/my/*/**のフォーマットであれば、myディレクトリの任意のディレクトリの任意のファイルがtestFunをトリガするという意味です.例えば/my/test/index.または/my/1/2/3/index.はtestFunをトリガします.この方法をトリガするので、ルートはページ出力しません.第二種類:つまり、私が/indexを訪問する時、index.ページを出力します.
第三種類:もし私がtest?v=indexと入力したら、出力のページはmyindexとなります.両方の*は数値が同じです.
第四種類:静的資源の取得に用いられます.私はpublic/bi*/**を訪問すると、public下の任意のファイルを出力します.例えば、私の要求経路はpublic/biz 009/styleesheets/css/main.cssです.それでは、経路が変換されたファイルの経路はpublic/stylistheets/css/main.cssです.
この四つの効果を実現する目的を持って、自分の実現を始めました.
第一段コードは、mimesの内容が長いので、代わりに…を使います.
まず正則を書いて、正則は主に***と**を置き換えるために使われ、正則文字列に置き換えられます.
次にRouterの構造関数を実現し、入ってきたパラメータを簡易的に処理し、着信したパラメータは直接上のオブジェクトでもいいし、jsonファイルのパスでもいいです.構造関数はevalでオブジェクトに変換します.JSON.parseを使わないのは、jsonフォーマットに対する要求が厳しいので、書きにくいからです.
後にイベントクラスを引き継ぎ、外部からのイベントバインディングを起動しやすくします.
"use strict";
var fs = require("fs");
var url = require("url");
var events = require("events");
var util = require("util");
var path = require("path");
var mimes = '...'.split(",");
var ALL_FOLDER_REG = /\/\*\*\//g;
var ALL_FOLDER_REG_STR = '/([\\w._-]*\/)*'; // XXX/XXX/XX/
var ALL_FILES_REG = /\*+/g;
var ALL_FILES_REG_STR = '[\\w._-]+'; // XX
var noop = function () {};
var Router = function (arg) {
this.methods = {};
if ((typeof arg == "object") && !(arg instanceof Array)) {
this.maps = arg;
} else if (typeof arg == "string") {
try {
var json = fs.readFileSync(arg).toString();
this.maps = eval('(' + json + ')');
} catch (e) {
console.log(e);
this.maps = {};
}
} else {
this.maps = {};
}
this.handleMaps();
};
//
util.inherits(Router, events.EventEmitter);
var rp = Router.prototype;
rp.constructor = Router;
上のコードの再構成関数では、ルーティングテーブルのルーティングアドレスとターゲットアドレスを処理してから、配列に保存するためのhandleMaps方法も実行されています.A_代表的な**B_*を表します.この二つも上記の正則文字列に対応しています.ALL_FOLDER_REG_STR 和 ALL_FILES_REG_STRrp.handleMaps = function () {
this.filters = []; //
this.address = []; //
for (var k in this.maps) {
var fil = trim(k);
var ad = trim(this.maps[k]);
fil = fil.charAt(0) == "/" ? fil : ("/" + fil);
ad = ad.replace(ALL_FOLDER_REG, '__A__').replace(ALL_FILES_REG, '__B__');
fil = fil.replace(/\?/g , "\\?").replace(ALL_FOLDER_REG, '__A__').replace(ALL_FILES_REG, '__B__');
this.filters.push(fil);
this.address.push(ad);
}
};
そして、functionを保存する方法も実現されます.ルーティングテーブルによって方法を実行するため、set方法があります.rp.set = function (name, func) {
if (!name)return;
this.methods[name] = (func instanceof Function) ? func : noop;
};
前のすべてが実現されたら、具体的なルート方法を実現します.このコードは比較的簡単です.要求が発生した時に、上記に保存されているルートアドレスを経由して、ルートアドレスの中の__u u u uA_同前B_対応する正規文字列に変換し、RegExpによって正規例を実施し、要求アドレスを一致させる.一致が成功した場合、現在のインデックスiは、ターゲットアドレスのインデックスとなります.そして文字列を分割し、urlであれば対応するurl処理を行い、functionであれば保存方法を実行し、req,resに入る.
rp.route = function (req, res) {
var urlobj = url.parse(req.url);
var pathname = urlobj.pathname;
var i = 0;
var match = false;
var fil;
for (; i < this.filters.length; i++) {
fil = this.filters[i];
var reg = new RegExp("^" + fil.replace(/__A__/g, ALL_FOLDER_REG_STR).replace(/__B__/g, ALL_FILES_REG_STR) + "$");
if (reg.test(fil.indexOf("?") >= 0 ? (pathname = urlobj.path) : pathname)) {
match = true;
break;
}
}
if (match) {
var ad = this.address[i];
var array = ad.split(':' , 2);
if(array[0] === "url"){
// url url
var filepath = getpath(fil , array[1] , pathname);
this.emit("match", filepath , pathname);
this.routeTo(res , filepath);
}else if(array[0] === "func" && (array[1] in this.methods)){
// func methods
this.methods[array[1]].call(this , req , res , pathname);
}else {
throw new Error("route Error");
}
}else {
this.emit("notmatch");
this.error(res);
}
};
上のコードの中にはgetpath方法があります.この方法は***と**を実際のアドレスにマッピングしても、/public/biz 009/styleesheets/css/main.cssをpublic/styless/mail.cssに変換するロジックです.function getpath(fil , ad , pathname){
var filepath = ad;
if(/__(A|B)__/g.test(fil) && /__(A|B)__/g.test(ad)){
var ay = fil.split("__");
var dy = ad.split("__");
var index = 0;
for(var k=0;k<ay.length;k++){
if(!ay[k]) continue;
var reg;
if (ay[k] === 'A' || ay[k] === 'B') {
reg = new RegExp(ay[k] === 'A' ? ALL_FOLDER_REG_STR : ALL_FILES_REG_STR);
// , AB , , dy , ay ,
while(index < dy.length){
if(dy[index] === 'A' || dy[index] === 'B'){
if(dy[index] === ay[k]){
dy[index] = pathname.match(reg)[0];
index++;
}
break;
}
index++;
}
} else {
reg = new RegExp(ay[k]);
}
pathname = pathname.replace(reg, '');
}
filepath = dy.join("");
}
filepath = path.normalize(filepath);
filepath = filepath.charAt(0) == path.sep ? filepath.substring(1,filepath.length):filepath;
return filepath;
}
実現原理を説明します.まずルートの住所と目標の住所を配列に変えます./public/bi*/**/* ==> ['/public/bi','B','','A','','B']
public/**/* ==> ['public','A','','B']
私が要請した時には/public/biz 009/styleesheets/css/main.cssになります.['/public/bi','B','','A','','B'] ==> ['/public/bi','z009','','/stylesheets/css/','','main.css']
そして上の「'public'、'A'、'、'B'」に対応します.['public','A','','B'] ==> ['public','/stylesheets/css/','','main.css']
実現ロジックは以下の通りです.要求されたpathnameはやはり/public/biz 009/styleesheets/css/main.cssです.スキャン['/public/bi','B',''A',''B']
スキャンの最初のものが'/public/bi'である場合、public/biを正則に変換し、マッチングによって/public/biz 009/stylistheets/css/main.cssを:z 009/styless/css/mail.cssに変更します.
二つ目のBをスキャンします.Bのために上のALL_を使います.FILES_REG_STRは「\w._-」+をマッチングさせてB対応のZ 009を取得し、同時にパス/styless/css/main.cssに変更してスキャンします. 「'public'、'、'、''B'」は、AまたはBにスキャンしたとき、対応するBではなくAなので、スキャンインデックスを更新しないので、上はindex+++ではなく、直接breakで、次のステップを継続します.
3番目の''をスキャンしますので、スキャンを続けても大丈夫です.
スキャンの4番目はAで、同じです.styleesheets/css/を取得して、pathnameをmain.cssにします.この時にスキャンします.「'public'、'、''A'、'、'B'」はスキャンインデックスがAに留まっています.その結果、両方ともAです.
その後、スキャンを続けます.スキャンが終わるまでは、「public」、「A」、「B」を「public」、「styleesheets/css/'、''main.css'」に変えます.
最後にまたjoinが出てきた結果:public/styless heets/css/main.cssは変換された最終パス、つまりマッチングされたファイルパスです.
もう一つの方法はファイルの内容を出力することです.一つは404です.比較的簡単です.詳細には説明しません.
rp.routeTo = function(res , filepath){
var that = this;
fs.stat(filepath , function(err , stats){
if(err || !stats.isFile()){
that.emit("error" , err || (new Error("path is not file")));
that.error(res);
return;
}
var fileKind = filepath.substring((filepath.lastIndexOf(".")+1)||0 , filepath.length);
var readstream = fs.createReadStream(filepath);
var index = mimes.indexOf('.'+fileKind);
var options = {
'Cache-Control':'no-cache',
'Content-Type': mimes[index+1]+';charset=utf-8',
'Content-Length':stats.size
};
res.writeHead(200, options);
readstream.pipe(res);
})
}
rp.error = function(res){
res.writeHead(404);
res.end("404 not found");
}
ソースはgithubにあります.https://github.com/whxaxes/easy-router