深入了解Express:挑战手写Express(1),实现路由

Express是Node.js服务端开发中一个比较完善的框架了,其中很多的思想理念还是很值得我们深入学习的,这一专题开始,我们开始尝试实现手写Express框架,对Express进行深入解析


  • 这里将以需求带动开发的方式逐步对Express进行实现,所以这里先简单写一个Express的例子:
const express = require("express")
const app = express();
app.get("/", function(req, res, next){
	console.log("1")
	next()
}, function(req, res, next){
	console.log("11")
	res.end("..../.....")
})
app.get("/test", function(req, res, next){
	res.end("..../test.....")
})
app.listen(3000, function(){
	console.log("Server Start on 3000")
})
  • 这里我们只写了一个很简单的例子,实现了两个get请求的接口,当然我们实现时会支持多种的请求方式,以上需求,就是我们将要实现路由机制的部分。

需求分析
Express路由实现中出现的几个类:

  1. Router类: 所有的路由都会在Router类中使用桟(实际就是一个数组)进行存储
  2. Route类: 一个路由就是一个Route类,里面会执行路由匹配的处理函数(handler)
  3. Laryer类: 路由的层,每个路由中可以传递多个处理函数和一个路由可以执行多种方法,如 “/user” 下可以有 “POST”, “PUT”, “DELETE”, "GET"等,这些都可以被放到一个处理层里.

以上三个类之间的关系如下如:
在这里插入图片描述

  • 首先先创建一个Application的类用于获取express的实例:
    express.js文件
const http = require("http")
const url = require("url")
const Application = require("./application")

function createApplication(){
    return new Application() 
}
module.exports = createApplication

application.js文件

const Router = require("./router")
const http = require("http")
const methods = require("./router/methods")
const slice = Array.prototype.slice;

function Application(){
	// 在一个express中就有一个Rputer的实例,可以使用app.get()之类的方式进行路由的创建
    this._router = new Router();
}

// 用于懒加载路由实例,因为在使用者没有使用路由时就没必要去创建
Application.prototype.lazyrouter = function(){
    if(!this._router){
        this._router = new Router()
    }
}

// 在application实例上绑定相应的路由处理的方法,这里的methods中是一个数组,存放了相应的方法名称(POST, GET, DELETE, PATCH, PUT等)
methods.forEach(function(method){
    Application.prototype[method] = function(){
        this.lazyrouter();

        // 这样可以支持多个处理函数,就是将一个路由处理方法中传递的所有的内容全部传给了相应的Router中的处理方法
        this._router[method].apply(this._router, slice.call(arguments));
        // 返回this的目的是可以使用串联的形式将多个方法串起来: app.get().post()这样的形式
        return this;
    }
})

Router类文件

const Route = require("./route")
const Layer = require("./layer")
const url = require("url")
const methods = require("./methods")
const slice = Array.prototype.slice;

function Router(){
	// 这是Router中路由处理的桟,里面放的是一个个的Layer(路由层)
	this.stack = [];
    return router;
}

// 创建一个Route的实例,向当前的路由系统中添加一个层
Router.prototype.route = function(path){
    let route = new Route(path);
    // Route中提供dispatch方法用于执行路由的相应的处理函数(handler),而该函数的执行放到了每一层中(Layer)
    let layer = new Layer(path, route.dispatch.bind(route));
    layer.route = route;
    // 将该层添加入栈
    this.stack.push(layer);
    return route;
}

methods.forEach(function(method){
    Router.prototype[method] = function(path){
        // 这里是在往Router里面添加一层,调用route方法
        let route = this.route(path);
        // 将相应路由的处理方法都传递给Route中定义的相应的方法,去掉第一个参数的原因是第一个参数应该是path值,不是处理函数
        route[method].apply(route, slice.call(arguments, 1))
    }
    return this;
})


// 调用handle方法,匹配stack中的每一层的路由执行相应的方法函数
Router.prototype.handle = function(req, res, out){
    let idx = 0, self = this;
    let {pathname} = url.parse(req.url, true)
    // 执行下一个路由层
    function next(){
    	// 如果stack中的路由都匹配完毕,则直接从Router路由层中出去
        if(idx >= self.stack.length){
            return out()
        }
        let layer = self.stack[idx++];
        // layer中定义match方法用于路由的匹配
        // handle_method是在Route中提供的方法,用于判断是否有该请求类型的处理函数(这样做的目的是为了加多匹配,一个相同的路由下可以有多个类型的处理方法)
        if(layer.match(pathname) && layer.route && layer.route.handle_method(req.method)){
        	// layer中提供handle_request()用于执行Route中的handler处理函数 
        	// 这里实际就是在创建Layer中传递进去的Route中的dispatch方法
           layer.handle_request(req, res, next);
        }else{
            next();
        }
    }
    next();
}

module.exports = Router;

Layer类

// 路由的每一层的类
function Layer(path, handler){
    this.path = path;
    this.handler = handler;
}

// 匹配路由
Layer.prototype.match = function(path){
    return this.path == path;
}

// 执行该层的路由处理函数
Layer.prototype.handle_request = function(req, res, next){
    this.handler(req, res, next)
}

module.exports = Layer;

Route类

const Layer = require("./layer")
const methods = require("./methods")
const slice = Array.prototype.slice;

// 路由类
function Route(path){
    this.path = path;
    this.stack = []; //每一个Route里面有一个stack处理函数层
    // 表示路由中有此方法的处理函数,这样的目的是为了加多匹配
    this.methods = {};
}

// Route中用于判断是否有相应类型的处理函数:如Get类型,POST类型等
Route.prototype.handle_method = function(method){
    method = method.toLowerCase();
    return this.methods[method];
}

methods.forEach(function(method){
    Route.prototype[method] = function(){
        let handlers = slice.call(arguments);
        this.methods[method] = true;
        for(let i = 0; i < handlers.length; i++){
        	// 在Route中的路由处理函数也是一个Layer
            let layer = new Layer("/", handlers[i]);
            layer.method = method;
            this.stack.push(layer)
        }
        return this;
    }
})


// 用于执行该层中的路由处理函数
Route.prototype.dispatch = function(req, res, out){
    let idx = 0, self = this;
    // 执行该层路由中的匹配函数
    function next(){
        if(idx >= self.stack.length){
            // 说明当前的Route已经走完了,就需要到Router中的下一层
            // 这里调用的out方法其实就是外层Router中传入进来的Router层的next方法,这算是express中设计比较巧妙的环节了
            return out()
        }
        // 每个方法的执行函数也是一个layer
        let layer = self.stack[idx++];
        // 判断有相应类型的处理函数匹配才调用执行,而这里传递到layer中给handle_request执行的方法就是用户创建路由时传递进来的方法
        if(layer.method === req.method.toLowerCase()){
            layer.handle_request(req, res, next)
        }else{
            next()
        }
    }
    next();
}

module.exports = Route

methods

const  methods = ["get", "post", "patch", "delete", "put"]

module.exports =  methods;

这里对路由只是做了第一层的处理,里面有些内容在之后会进行删改,这里今天的目的就是进行路由的第一次抽离,当然,其中get,post之类的路由方法中的请求数据处理等会在之后解决。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值