zoukankan      html  css  js  c++  java
  • express极简实现

    express极简实现

    let http = require('http');
    let url = require('url');
    let createApplication = () => {
        let app = (req, res) => {
            /* 获取路径和方法 */
            let { pathname } = url.parse(req.url, true);
            let m = req.method.toLowerCase();
            /* index记录next执行次数 */
            let index = 0;
            function next(err) {
                //遍历完方法都没有返回页面
                if (index === app.routes.length)
                    return res.end(`Cannot ${m} ${pathname}`);
                let { method, path, handler } = app.routes[index++];
                if (err) {/* 错误处理 */
                    if (handler.length === 4) {
                        handler(err, req, res, next);
                    } else {
                        next();
                    }
                } else {
                    /* 判断是路由还是中间件 */
                    if (method === 'middle') {
                        if (path === '/' || path === pathname ||
                            pathname.startsWith(path + '/')) {/* 开头路由相同 */
                            //执行中间件的回调,跳过错误处理中间件
                            handler.length === 4 ? next() : handler(req, res, next);
                        } else {
                            next();//不匹配遍历下一个
                        }
                    } else {/* 路由 */
                        /* 方法、路径相同执行回调,不同则判断下一个routes */
                        if ((method === m || method === 'all') &&
                            (path === pathname || path === '*')) {
                            handler(req, res);
                        } else { next(); }
                    }
                }
            }
            next();/* 默认先执行一次 */
        }
        /* 把请求方法存入数组 */
        app.routes = [];
        http.METHODS.forEach(method => {
            /* 变为小写 */
            method = method.toLowerCase();
            app[method] = (path, handler) => {
                /* 将方法路径回调存到数组 */
                let layer = { method, path, handler };
                app.routes.push(layer);
            }
        })
        /* all方法监听所有请求 */
        app.all = (path, handler) => {
            let layer = { method: 'all', path, handler }
            app.routes.push(layer);
        }
        /* 中间件 */
        app.use = (path, handler) => {
            /* 若没有传递path */
            if (typeof handler !== 'function') {
                handler = path;
                path = '/';
            }
            app.routes.push({ method: 'middle', path, handler });
        }
        /* 监听端口 */
        app.listen = function () {
            /* 创建服务并监听 */
            let sever = http.createServer(app);
            sever.listen(...arguments);
        }
        return app;
    }
    module.exports = createApplication;
    

    1. 实现端口监听

    创建服务后通过argument传参即可

    app.listen = function () {
        /* 创建服务并监听 */
        let sever = http.createServer(app);
        sever.listen(...arguments);
    }
    

    2. 实现请求

    1. 将所有请求method,path,handler放到routes数组中,执行时根据请求头中获得的methodpathname得到对应的handler,然后执行handler
    /* 把普通请求方法存入数组 */
    app.routes = [];
    http.METHODS.forEach(method => {
        /* 变为小写 */
        method = method.toLowerCase();
        app[method] = (path, handler) => {
            /* 将方法路径回调存到数组 */
            let layer = { method, path, handler };
            app.routes.push(layer);
        }
    })
    
    1. app.all('*',handler)表示所有方法所有路由都可以触发,all表示所有方法,*表示所有路由,为了实现这一功能可以将all作为一个特殊的method*作为一个特殊路径,减少条件约束
    /* -------------设置特殊的method------------- */
    app.all = (path, handler) => {
        let layer = { method: 'all', path, handler }
        app.routes.push(layer);
    }
    /* -------------执行时的判断条件------------- */
    /* 获取路径和方法 */
    let { pathname } = url.parse(req.url, true);
    let m = req.method.toLowerCase();
    app.routes.forEach(v => {
        let { method, path, handler } = v;
        /* 方法、路径相同执行回调,不同则判断下一个routes */
        if ((method === m || method === 'all') &&
            (path === pathname || path === '*')) {
            handler(req, res);
        } else {
            next();
        }
    })
    

    3.中间件实现

    1. 实现拦截功能
    (1)设置app.use默认路径,并且方法记录为middle,后续可以针对method特殊处理

    app.use = (path, handler) => {
        /* 若没有传递path */
        if (typeof handler !== 'function') {
            handler = path;
            path = '/';
        }
        app.routes.push({ method: 'middle', path, handler });
    }
    

    (2)设置next函数,index记录已经遍历routes数组个数,等于数组长度还未返回页面,说明不存在该页面
    (3)根据method判断是否为中间件,然后匹配路由,若路由匹配成功则执行handerhandler内调用了next才会继续往下执行next,从而实现拦截功能

    let index=0;
    function next() {
        //遍历完都没有返回页面
        if (index === app.routes.length)
            return res.end(`Cannot ${m} ${pathname}`);
        let { method, path, handler } = app.routes[index++];
        /* 判断是路由还是中间件 */
        if (method === 'middle') {
            if (path === '/' || path === pathname ||
                pathname.startsWith(path + '/')) {/* 开头路由相同 */
                //执行中间件的回调,跳过错误处理中间件
                handler.length === 4 ? next() : handler(req, res, next);
            } else {
                next();//不匹配遍历下一个
            }
        } else {/* 路由 */
            /* 方法、路径相同执行回调,不同则判断下一个routes */
            if ((method === m || method === 'all') &&
                (path === pathname || path === '*')) {
                handler(req, res);
            } else { 
                next(); 
            }
        }
    }
    next();/* 默认先执行一次 */
    
    示例:
    /* 中间件处理数据 */
    app.use('/detail', (req, res, next) => {
        console.log('详情1');
        res.setHeader('Content-Type', 'text/html;charset=utf-8')
        next();/* 控制是否往下执行 */
    })
    app.get('/detail/a', (req, res) => {
        console.log('详情2');
        res.end('详情');
    })
    

    2. 错误处理跳转
    当某个中间件内部给next传参后,next会跳过中间的步骤,一直传递参数往后找,直到找到错误处理中间件,执行错误处理handler函数.

    function next(err) {
        //省略...
        if (err) {/* 错误处理中间件传递4个参数 */
            if (handler.length === 4) {
                handler(err, req, res, next);
            } else {
                next(err);
            }
        } else {
            //省略...
        }
    }
    示例:
    /* next传递错误后,直接运行错误中间件(含4个传入参数), */
    app.use('/err', (req, res, next) => {
        let err = "err1";
        console.log(err)
        next(err);
    })
    app.get('/err/v', (req, res) => {
        res.end('errv');
    })
    app.use((err, req, res, next) => {/* 错误处理 */
        console.log(err);
        res.end(err)
    })
    /* 
    输入网址 http://localhost:8888/err/v
    输出 err1 err1
    */
    
  • 相关阅读:
    oracle minus 与sqlserver except
    Ext.form.FieldSetI(转)
    C#系统服务定时执行(转)
    extjs4.0的数据代理proxy及数据模型的使用(转)
    JS属性defer的好处及IE8 提示 KB927917, IE6 IE7 提示操作已中止的解决办法
    水晶报表打印及多个报表打印到一个PDF文件里的办法
    C#不添加引用,动态调用webservice(转)
    向 ReportViewer 报表中添加页眉和页脚,控制页眉显示变量的值
    网站主要使用jquery总结(转)
    JSBuilder2使用方法(转)
  • 原文地址:https://www.cnblogs.com/aeipyuan/p/12783490.html
Copyright © 2011-2022 走看看