深入理解express框架的匹配路由

栏目: Node.js · 发布时间: 6年前

内容简介:现在node的web框架有很多,除express 还有koa egg等等。 但它们本质上还是基于原生node框架的http。其实框架都大差不差,主要是观摩和学习。本篇文章主要记录下自己在node爬坑之路上的经历和收获~本文主要实现express的功能之一, 匹配路由首先我们看一下express:

现在node的web框架有很多,除express 还有koa egg等等。 但它们本质上还是基于原生node框架的http。其实框架都大差不差,主要是观摩和学习。本篇文章主要记录下自己在node爬坑之路上的经历和收获~

本文主要实现express的功能之一, 匹配路由

  • 匹配简单静态路由
  • 匹配动态路由

首先我们看一下express:

const express = require('express');

let app = new express();

app.get('/',(req,res)=>{
    res.end('home Page.');
});

app.get('/center',(req,res)=>{
    res.end('center Page.');
});

/** 匹配到动态路由 获取路由参数并返回 */
 app.get('/product/:id/:name',(req,res)=>{
     res.end(JSON.stringify(req.params));
 });

/** 当以上路径都没有匹配成功时 返回404 */
app.all('*',(req,res)=>{
    res.end('404');
});

let port = 3000;

app.listen(port,()=>{
    console.log(`Server is start on port ${port}`);
});

ok.代码很简单。引入express,new了个express实例,写了几个路由,最后启了本地服务。

代码第二行 我们把引入的express 给new出来,说明express内部返回的是一个function。

好.麻雀虽小 五脏俱全,我们今天就来实现express的这些功能。

let http = require('http'); /** express基于http */
let url = require('url'); /** 用来解析请求的路径 */
/** express引入了methods 它的作用是返回各种的请求方法 */
let methods = require('methods');

function application(){
    /** 1 express返回了一个函数 
     * 这个函数就是http.createServer的监听函数
    */
    let app = (req,res) => {
        /** 1.1 url模块解析 拿到请求路径 比如 /user */
        let { pathname } = url.parse(req.url);
        /** 1.2 拿到请求方法 方法是大写 记得转换为小写 */
        let requestMethod = req.method.toLowerCase();
        /** 1.3 通过拿到的路径和方法 在之前定义好的路由数组routes中 循环去匹配 */
        for(let i = 0; i < app.routes.length; i++){
            /** 1.4 解构 拿到每一个路由的 路径 方法 回调 */
            let { path, method, cb } = app.routes[i];
            if((pathname===path||path==='*') && (requestMethod===method)||method==='all'){
                /** 1.5 如果匹配到 返回回调并执行 */
                return cb(req,res);
            }
        }
        /** 1.6 没有匹配到任何路由 */
        res.end(`Cannot found ${pathname}/${requestMethod}`);
    }

    /** 2 定义一个存放所有路由的数组 */
    app.routes = [];
    /** 2.1 往methods数组中添加一个方法 all  并循环数组 */
    [...methods,'all'].forEach((method)=>{
        app[method] = function(path,cb){
            /** 2.2 先将每个请求的路由地址 方法和回调保存起来 
             * path:路径  method:方法   cb:回调
            */
            let layer = { path, method, cb };
            app.routes.push(layer);
        }
    });

    /** 3 监听端口 */
    app.listen = function(...arguments){
        /** 3.1 利用http的createServer方法 将app传进去 */
        let server = http.createServer(app);
        server.listen(...arguments);
    }
    return app;
}

/** 4 将方法导出出去 */
module.exports = application;

代码上面都仔细的标注了观看序号,1.2.3... 按照顺序观看即可。

我们手写的整个express就是一个函数 函数里面return了一个函数。通过node原生框架http的方法 包装了该函数,最后再将整个函数module.exports导出出去。

最后我们启动项目,通过浏览器或者postman调用接口,发现确实能实现部分的express功能,但是有一点,此时我们能实现的仅仅是静态的路由,如果有路由参数的情况下,比如/product/:id/:name。结果就不符合预期。 改造:

代码上面都仔细的标注了观看序号,1.2.3... 按照顺序观看即可。

let http = require('http');
let url = require('url');
let methods = require('methods');

function application(){
    let app = (req,res) => {
        let { pathname } = url.parse(req.url);
        let requestMethod = req.method.toLowerCase();
        for(let i = 0; i < app.routes.length; i++){
            let { path, method, cb } = app.routes[i];
            /** 7 如果请求路径path中 就说明该路由是动态的 */
            if(path.params){
                /** 8 匹配该动态路由后面的动态参数 匹配成功返回true */
                if(path.test(pathname)){
                    /** 9 解构赋值 拿到动态路由的参数 */
                    let [, ...otherParams] = pathname.match(path);
                    /** 10 通过reduce()方法 将路由参数转换为对象形式
                     * 并放到req.params中
                     */
                    req.params = path.params.reduce(
                        (memo,key,index)=>(
                            memo[key]=otherParams[index],memo
                        ),{}
                    );
                    /** 11 返回匹配到的动态路由 */
                    return cb(req,res);
                }
            }

            if((pathname===path||path==='*') && (requestMethod===method)||method==='all'){
                return cb(req,res);
            }
        }
        res.end(`Cannot found ${pathname}/${requestMethod}`);
    }

    app.routes = [];
    [...methods,'all'].forEach((method)=>{
        app[method] = function(path,cb){
            let layer = { path, method, cb };

            /** 1 定义一个空数组 来存放动态路由的参数 */
            let params = [];
            /** 2 如果路径中包含: 说明该路由是动态路由 */
            if(path.includes(':')){
                /** 3 更改该动态路由的路径path为一个正则表达式
                 * 目的是为了等真正请求到来时 匹配到该动态路由 并拿到路由参数
                 */
                layer.path = new RegExp(path.replace(/:([^\/]*)/g,function(){
                    /** 4 将动态路由参数的key 放入params数组中 */
                    params.push(arguments[1]);
                    /** 5 返回了一个正则来匹配真正的动态路由参数 注意此处没有: */
                    return '([^\/]*)';
                }));
                /** 6 把解析到的动态路由放到该路由路径path的params上 */
                layer.path.params = params;
            }

            app.routes.push(layer);
        }
    });

    app.listen = function(...arguments){
        let server = http.createServer(app);
        server.listen(...arguments);
    }

    return app;
}

module.exports = application;

先通过正则匹配到该动态路由,并把该动态路由的path替换为一个正则,放到数组中,等待真正的动态路由到来时,从路由数组中拿到该动态路由的路径,也就是刚才替换的正则,来匹配该动态路由后的参数即可。

通过以上就能实现获取动态路由的参数 上图:

深入理解express框架的匹配路由

代码在git mock-express


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

A Guide to Monte Carlo Simulations in Statistical Physics

A Guide to Monte Carlo Simulations in Statistical Physics

Landau, David P./ Binder, Kurt / Cambridge Univ Pr / 2005-9 / 786.00元

This new and updated edition deals with all aspects of Monte Carlo simulation of complex physical systems encountered in condensed-matter physics, statistical mechanics, and related fields. After brie......一起来看看 《A Guide to Monte Carlo Simulations in Statistical Physics》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具