connect 主要的思路是给app函数添加一个stack的数组在数组中存放路由path以及handle方法的对象,通过向handle方法中传入next方法去把控制权交给执行的方法,我们可以选择next()执行这个下一个stack中的方法或是不调用结束本次请求。 下面我会详细讲解下这两个方法
创建一个app方法
function createServer() {
function app(req, res, next) {
app.handle(req, res, next);
}
// 添加方法
merge(app, proto);
merge(app, EventEmitter.prototype);
app.route = '/';
// 存放中间处理方法的栈
app.stack = [];
return app;
}
复制代码
每调用一次createServe方法就会生成一个app函数,然后通过proto 这个对象去给app添加一些方法。 下面的use方法就是给主流程添加中间件的方法。将处理方法存在stack的数组中,请求过来之后一次调用stack中的方法。
proto.use = function use(route, fn) {
var handle = fn;
var path = route;
// 默认设置route 为 '/'
if (typeof route !== 'string') {
handle = route;
path = '/';
}
// 如果对象有handle方法取对象的handle方法
if (typeof handle.handle === 'function') {
var server = handle;
server.route = path;
handle = function(req, res, next) {
server.handle(req, res, next);
};
}
// 如果是http的对象,获取监听的http的request的第一个事件方法
if (handle instanceof http.Server) {
handle = handle.listeners('request')[0];
}
// 去除路由最后一个/ 字符
if (path[path.length - 1] === '/') {
path = path.slice(0, -1);
}
// 将处理方法和路由压入app栈
this.stack.push({ route: path, handle: handle });
// 返回当前对象
return this;
};
复制代码
proto.handle 方法是用来调用中间件处理方法的程序
proto.handle = function handle(req, res, out) {
var index = 0;
// 获取url的host
var protohost = getProtohost(req.url) || '';
var removed = '';
var slashAdded = false;
var stack = this.stack;
// 对http请求去做最后一个处理的方法
var done = out || finalhandler(req, res, {
env: env,
onerror: logerror
});
// 保存原始的url
req.originalUrl = req.originalUrl || req.url;
// 调用下一个处理函数,这个很重要
function next(err) {
if (slashAdded) {
// 去掉一开始的'/'
req.url = req.url.substr(1);
slashAdded = false;
}
if (removed.length !== 0) {
// 还原url
req.url = protohost + removed + req.url.substr(protohost.length);
removed = '';
}
// 下一个处理方法
var layer = stack[index++];
// 调用处理结束方法
if (!layer) {
defer(done, err);
return;
}
// 获取路径
var path = parseUrl(req).pathname || '/';
var route = layer.route;
// 如果路径与路由不匹配则继续调用下一个方法处理
if (path.toLowerCase().substr(0, route.length) !== route.toLowerCase()) {
return next(err);
}
// skip if route match does not border "/", ".", or end
var c = path[route.length];
if (c !== undefined && '/' !== c && '.' !== c) {
return next(err);
}
// trim off the part of the url that matches the route
if (route.length !== 0 && route !== '/') {
removed = route;
// 截断url
req.url = protohost + req.url.substr(protohost.length + removed.length);
// ensure leading slash
if (!protohost && req.url[0] !== '/') {
req.url = '/' + req.url;
slashAdded = true;
}
}
// 调用中间件的处理方法
call(layer.handle, route, err, req, res, next);
}
next();
};
复制代码