# express_test **Repository Path**: aeipyuan/express_test ## Basic Information - **Project Name**: express_test - **Description**: No description available - **Primary Language**: NodeJS - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2020-04-25 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 简单实现express中路由和中间件 ``` javascript 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`传参即可 ``` javascript app.listen = function () { /* 创建服务并监听 */ let sever = http.createServer(app); sever.listen(...arguments); } ``` ## 2. 实现请求 1. 将所有请求`method`,`path`,`handler`放到`routes`数组中,执行时根据请求头中获得的`method`和`pathname`得到对应的`handler`,然后执行`handler` ``` javascript /* 把普通请求方法存入数组 */ app.routes = []; http.METHODS.forEach(method => { /* 变为小写 */ method = method.toLowerCase(); app[method] = (path, handler) => { /* 将方法路径回调存到数组 */ let layer = { method, path, handler }; app.routes.push(layer); } }) ``` 2. `app.all('*',handler)`表示所有方法所有路由都可以触发,`all`表示所有方法,`*`表示所有路由,为了实现这一功能可以将`all`作为一个特殊的`method`,`*`作为一个特殊路径,减少条件约束 ``` javascript /* -------------设置特殊的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(); } }) ``` ## 中间件实现 **1. 实现拦截功能** (1)设置`app.use`默认路径,并且方法记录为`middle`,后续可以针对`method`特殊处理 ``` javascript 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`,从而实现拦截功能 ``` javascript 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`函数. ``` javascript 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 */ ```