上面这张图画了express的路由处理方式,这也是和koa最大的区别,栈上有一个个的路径以及处理方法,每个路径对应一个层,每个层里又会有处理方法或者路由,每个层里都有一个next用来传递,这相当于一个二维数组。
路由的实现
在使用express的时候,我们可以通过如下的方式来注册路由:
app.get("/",function(req,res){
res.send("hello world");
});
实现路由中的"层":
首先要理解一个前面所说的层的概念,简单点,定义路由的每个get都是一层,layer.js模块主要是实现这个层,layer中有两个参数:path和处理函数
//layer.js
const pathToRegexp = require('path-to-regexp');
function Layer(path, handler) {
//path:路径 handler:处理函数
this.path = path;
this.handler = handler;
this.keys = [];
this.regexp = pathToRegexp(this.path, this.keys);
}
//判断层和传入的路径是否匹配
Layer.prototype.match = function (path) {
if (this.path == path) {
return true;
}
if (!this.route) {
return path.startsWith(this.path + '/');
}
//如果这个Layer是一个路由的层
if (this.route) {
let matches = this.regexp.exec(path);
if (matches) {
this.params = {};
for (let i = 1; i < matches.length; i++) {
let name = this.keys[i - 1].name;
let val = matches[i];
this.params[name] = val;
}
return true;
}
}
return false;
}
Layer.prototype.handle_request = function (req, res, next) {
this.handler(req, res, next);
}
Layer.prototype.handle_error = function (err, req, res, next) {
//错误处理函数
if (this.handler.length != 4) {
return next(err);
}
this.handler(err, req, res, next);
}
module.exports = Layer;
处理请求的路由
router中的index.js:根据文章开始画的图,router中应该有一个数组,也就是stack,当我们调用这里的route方法的时候会创建一个层(layer)并将path传进去,并调用route.dispatch交给route去处理,route主要用来创建一个route实例并挂到layer上并执行这个层,这里还定义了处理中间件和子路由的handle方法以及处理路径参数的process_params方法.
const Route = require('./route');
const Layer = require('./layer');
const url = require('url');
const methods = require('methods');
const init = require('./init');
const slice = Array.prototype.slice;
function Router() {
function router(req, res, next) {
router.handle(req, res, next);
}
Object.setPrototypeOf(router, proto);
router.stack = [];
router.paramCallbacks = {};//缓存路径参数和处理函数
//加载内置中间件
router.use(init);
return router;
}
let proto = Object.create(null);
//创建一个Route实例并添加层
proto.route = function (path) {
let route = new Route(path);
let layer = new Layer(path, route.dispatch.bind(route));
layer.route = route;
this.stack.push(layer);
return route;
}
proto.use = function (path, handler) {
if (typeof handler != 'function') {
handler = path;
path = '/';
}
let layer = new Layer(path, handler);
layer.route = undefined;//通过layer有没有route来判断是一个中间件函数还是一个路由
this.stack.push(layer);
}
methods.forEach(function (method) {
proto[method] = function (path) {
let route = this.route(path);//向Router里添一层
route[method].apply(route, slice.call(arguments, 1));
return this;
}
});
proto.param = function (name, handler) {
if (!this.paramCallbacks[name]) {
this.paramCallbacks[name] = [];
}
this.paramCallbacks[name].push(handler);
}
/**
* 1.处理中间件
* 2. 处理子路由容器
*/
proto.handle = function (req, res, out) {
let idx = 0, self = this, slashAdded = false, removed = '';
let { pathname } = url.parse(req.url, true);
function next(err) {
if (removed.length > 0) {
req.url = removed + req.url;
removed = '';
}
if (idx >= self.stack.length) {
return out(err);
}
let layer = self.stack[idx++];
if (layer.match(pathname)) {
if (!layer.route) {
removed = layer.path;
req.url = req.url.slice(removed.length);
if (err) {
layer.handle_error(err, req, res, next);
} else {
layer.handle_request(req, res, next);
}
} else {
if (layer.route && layer.route.handle_method(req.method)) {
req.params = layer.params;
self.process_params(layer, req, res, () => {
layer.handle_request(req, res, next);
});
} else {
next(err);
}
}
} else {
next(err);
}
}
next();
}
proto.process_params = function (layer, req, res, out) {
//路径参数方法
let keys = layer.keys;
let self = this;
//用来处理路径参数
let paramIndex = 0 /**key索引**/, key/**key对象**/, name/**key的值**/, val, callbacks, callback;
//调用一次param意味着处理一个路径参数
function param() {
if (paramIndex >= keys.length) {
return out();
}
key = keys[paramIndex++];//先取出当前的key
name = key.name;
val = layer.params[name];
callbacks = self.paramCallbacks[name];// 取出等待执行的回调函数数组
if (!val || !callbacks) {//如果当前的key没有值,或者没有对应的回调就直接处理下一个key
return param();
}
execCallback();
}
let callbackIndex = 0;
function execCallback() {
callback = callbacks[callbackIndex++];
if (!callback) {
return param();
}
callback(req, res, execCallback, val, name);
}
param();
}
module.exports = Router;
router中的route.js:一个路由类,每当get的时候创建一个路由对象,这里的dispatch是主要的业务处理方法
const Layer = require('./layer');
const methods = require('methods');
const slice = Array.prototype.slice;
function Route(path) {
this.path = path;
this.stack = [];
this.methods = {};
}
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++) {
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(err) {
if (err) {
//错误处理,如果出错就跳过当前路由
return out(err);
}
if (idx >= self.stack.length) {
return out();
}
//取出一层
let layer = self.stack[idx++];
if (layer.method == req.method.toLowerCase()) {
layer.handle_request(req, res, next);
} else {
next();
}
}
next();
}
module.exports = Route;
application.js
这个返回一个Applition类,这里先定义了一个lazyrouter方法,这个方法主要是用来懒加载,如果不调get的话就没有初始化的必要所以需要个懒加载;还有listen方法主要用来创建服务器并监听,这里面的done主要是处理没有路由规则和请求匹配。
const http = require('http');
const Router = require('./router');
const methods = require('methods');
function Application(){
this.settings = {};//保存参数
this.engins = {};//保存文件扩展名和渲染函数
}
Application.prototype.lazyrouter = function () {
if (!this._router) {
this._router = new Router();
}
}
Application.prototype.param = function (name, handler) {
this.lazyrouter();
this._router.param.apply(this._router, arguments);
}
Application.prototype.set = function (key, val) {
//设置,传一个参数表示获取
if (arguments.length == 1) {
return this.settings[key];
}
this.settings[key] = val;
}
//定义文件渲染方法
Application.prototype.engine = function (ext, render) {
let extension = ext[0] == '.' ? ext : '.' + ext;
this.engines[extension] = render;
}
methods.forEach(function (method) {
Application.prototype[method] = function () {
if (method == 'get' && arguments.length == 1) {
return this.set(arguments[0]);
}
this.lazyrouter();
//此处是为了支持多个处理函数
this._router[method].apply(this._router, slice.call(arguments));
return this;
}
});
Application.prototype.route = function (path) {
this.lazyrouter();
//创建一个路由,然后创建一个layer ,layer.route = route.this.stack.push(layer)
this._router.route(path);
}
//添加中间件,而中间件和普通的路由都是放在一个数组中的,放在this._router.stack
Application.prototype.use = function () {
this.lazyrouter();
this._router.use.apply(this._router, arguments);
}
Application.prototype.listen = function () {
let self = this;
let server = http.createServer(function (req, res) {
function done() {
//如果没有任何路由规则匹配的话会走此函数
res.end(`Cannot ${req.method} ${req.url}`);
}
self._router.handle(req, res, done);
});
server.listen(...arguments);
}
module.exports = Application;
这里介绍几个主要的模块,全部代码以及测试用例请见Github