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. 实现请求
- 将所有请求
method
,path
,handler
放到routes
数组中,执行时根据请求头中获得的method
和pathname
得到对应的handler
,然后执行handler
/* 把普通请求方法存入数组 */
app.routes = [];
http.METHODS.forEach(method => {
/* 变为小写 */
method = method.toLowerCase();
app[method] = (path, handler) => {
/* 将方法路径回调存到数组 */
let layer = { method, path, handler };
app.routes.push(layer);
}
})
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
判断是否为中间件,然后匹配路由,若路由匹配成功则执行hander
,handler
内调用了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
*/