expressでのルートと中間部品

46722 ワード

expressはnodejsのhttpモジュールパッケージに基づくフレームです。
以下は極めて簡単な用例である。
const express = require("express");
const app = express();

app.param('userid', function(req, res, next, userid) {
    req.user = getUser(userid);
    next();
});

app.use("/water",function(req,res,next){
    console.log("middle");
    next();
});

function getUser(userId){
    return {
        userId, 
        age:8,
        name:"freemen"
    }
}

function setUser(user){

}

app.get('/username/:userid/:name',function(req,res){
    console.log(req.user);
    console.log(req.params);
    req.user.name = req.params.name;
    setUser(req.user);
    res.end("update username success!");
});

app.get('/username/:userid/:age',function(req,res){
    req.user.age = req.params.age;
    setUser(req.user);
    res.end("update username success!");
});


app.get("/user",function(req,res){
    console.log(req.query);
    console.log(req.path);
    console.log(req.hostname);
});

app.listen(8888);
これらの機能を実装してみます。主にルートとミドルパーツです。 http Server関数をエクスポートしてこの関数にlistenという属性を定義します。彼がやるべきことは呼び出し時にhttpを使ってサービスを開始し、対応するポートを傍受します。
 app.listen = (...rest)=>{
        const server = http.createServer(app);
        server.listen(...rest);
  }
ルーティングと中間部品は同じキューに配置され、 でループバック関数が実行される。ルートと中間部品の違いは、中間部品が反復の権利すなわちnext関数を得て、そしてこのnext関数がエラー値を伝えた時にこのエラー値をクリアします。
ルーティングパラメータの処理について
app.param('userid', function(req, res, next, userid) {
    req.user = getUser(userid);
    next();
});
まず、このような処理パラメータが必要な場合と普通のルーティングの唯一の違いを分析すると、ルーティングのアドレスに違いがある/username/:userid/:nameとは、:の後の値を処理する必要があるということです。定義するときに有用な値を保存します。
http.METHODS.forEach( method => {
        const methods = method.toLocaleLowerCase();
        app[ methods ] = function(path,cb){
            const layer = { methods, path, cb };
            if(path.includes(":")){
               let paramsNames = [];
               path = path.replace(/:([^\/]+)/g,function(){
                   paramsNames.push(arguments[1]);
                   return '([^\/]+)'
               })
               layer.path = new RegExp(path);
               layer.paramsNames = paramsNames;
            }
            app.routers.push(layer)
        }
    })
その後、オブジェクトを定義して、これらのparamのパラメータキーの名前をルートのパラメータキーとして保存します。
app.paramhandlers = {};
app.param = function(name,handler){
    app.paramhandlers[name] = handler;
}
その後、反復時に、対応する判定ロジックを追加して、前に保存したパラメータを取り出し、対応する値を送る。
if(route.paramsNames){
                        let machers = pathname.match(path);
                        if(machers){
                            let params = {};
                            for(let i=0; i < route.paramsNames.length;i++){
                                params[route.paramsNames[i]] = machers[i+1];
                            }
                            req.params = params;
                            for(let j=0;j<route.paramsNames.length;j++){
                                let name = route.paramsNames[j];
                                let handle = app.paramhandlers[route.paramsNames[j]];
                                if(handle){
                                    return handle(req,res,()=>route.cb(req,res),req.params[name]);
                                }
                            }
                        }else{
                            next();
                        }
                    }
完全に次の通り実現します
const http = require("http");
const url = require("url");
//                          listen use param                  app       
function httpServer(){
    const app = (req,res)=>{
        const { pathname } = url.parse(req.url,true);
        let index = 0;
        function next(err){
            if(index >= app.routers.length){
                return res.end(`cannot find ${req.method}---${pathname}`);
            }
            let route = app.routers[index++];
            const { methods ,cb ,path} = route;
            if(err){
                if( methods == "middle" ){
                    if(path=='/'||pathname.startsWith(path+"/")||path==pathname){
                        if(cb.length==4){
                           cb(err,req,res,next);
                        }else{
                            next(err);
                        }
                    }
                }else{
                    next(err);
                }
            }else{
                if( methods=="middle" ){
                    cb(req,res,next);
                }else{
                    if(route.paramsNames){
                        let machers = pathname.match(path);
                        if(machers){
                            let params = {};
                            for(let i=0; i < route.paramsNames.length;i++){
                                params[route.paramsNames[i]] = machers[i+1];
                            }
                            req.params = params;
                            for(let j=0;j<route.paramsNames.length;j++){
                                let name = route.paramsNames[j];
                                let handle = app.paramhandlers[route.paramsNames[j]];
                                if(handle){
                                    return handle(req,res,()=>route.cb(req,res),req.params[name]);
                                }
                            }
                        }else{
                            next();
                        }
                    }
                    if((route["path"] == pathname||route["path"]=="*")&&(req.method.toLocaleLowerCase() == route["methods"]|| route['methods']== "all" )){
                        return cb(req,res);
                    }else{
                        next();
                    }
                }
            }
        }
        next();
    }

    app.paramhandlers = {};
    app.param = function(name,handler){
        app.paramhandlers[name] = handler;
    }

    app.routers = [];

    app.listen = (...rest)=>{
        const server = http.createServer(app);
        server.listen(...rest);
    }
    
    http.METHODS.forEach( method => {
        const methods = method.toLocaleLowerCase();
        app[ methods ] = function(path,cb){
            const layer = { methods, path, cb };
            if(path.includes(":")){
               let paramsNames = [];
               path = path.replace(/:([^\/]+)/g,function(){
                   paramsNames.push(arguments[1]);
                   return '([^\/]+)'
               })
               layer.path = new RegExp(path);
               layer.paramsNames = paramsNames;
            }
            app.routers.push(layer)
        }
    })

    app.all = function(path,cb){
        app.routers.push({
            methods:"all",
            path,
            cb
        })
    }
    app.use = function(path,cb){
        if(typeof cb !=="function"){
            let middle = path;
            cb = middle;
            path = '/';
        }
        app.routers.push({
            methods:"middle",
            path,
            cb
        })
    }
    //                   
    app.use(function(req,res,next){
        const urlObj = url.parse(req.url,true);
        req.query  = urlObj.query;
        req.path  = urlObj.pathname;
        req.hostname  = req.headers['host'].split(":")[0];
        next();
    })
    return app;
}

module.exports = httpServer;