express 中文翻译 “快递”
- 基于http服务进行了二次封装
- 在express中扩展了很多 中间件
- 对路由进行了封装
- req,res 进行了一些功能的封装
关于express和koa中间件的区别
- koa 小巧,仅做基础封装
- express 有很多额外的中间件,路由
简单使用
需要安装npm install express
express.js
let express = require('../node_modules/express');let app = express();app.get('/',(req,res)=>{ res.end('ok'+req.money);});app.get('/hello', (req, res) => { res.end('hello');});app.delete('/hello', (req, res) => { res.end('delete hello');});app.post('/hello',(req,res)=>{ res.end('post hello');});app.all('*',(req,res)=>{ res.end('end');});// 发送静态文件app.get('/404',function (req,res) { res.sendFile('404.html',{ root:__dirname});//必须保证绝对路径 //res.sendFile(require('path').join(__dirname,'404.html'));})app.listen(3000);复制代码
上述我们可以看出:
- express是一个函数
- 函数有 delete, post,all等,处理路由
- express执行后 返回的是一个监听函数
- 该函数可以起服务
- 在访问路由之前可以做很多事情 => 中间件
express架子 express/index.js
let http = require('http');let url = require('url');function createApplication(){ //若果请求方法,路径一致,则执行回调 let app = (req,res) => { let reqMethod = req.method.toLowerCase(); let {pathname} = url.parse(req.url,true); for(let i=0;i{ app.routes.push({ method:'get', path, handler }) } app.listen = (...args) => { let server = http.createServer(app); server.listen(...args); } return app;}module.exports = createApplication复制代码
post测试:可以下载postman插件
all
function createApplication(){ ... let app = (req,res) => { ... for(let i=0;i
洋葱模型
express/test1.js
function app(){}app.routes = [];app.use = function(cb){ app.routes.push(cb)}app.use((next)=> { console.log(1); next(); console.log(2);})app.use((next)=> { console.log(3); next(); console.log(4);}) app.use((next)=> { console.log(5); console.log(6);}) let index = 0;function next(){ if(index === app.routes.lenth) return; app.routes[index++](next)}next();复制代码
输出135642,这就是我们说的洋葱模型
中间件
- 中间件作用 当我们真正访问的路由之前可以干很多事情
- 可以决定是否向下执行
- 可以做一些权限判断,可以写多个中间件,和路由是放在同一个队列中的
- req是同一个
- 类似于洋葱模型
express.js
app.use('/', (req,res,next)=> { req.money = 10000; next();// 继续的意思,不调用next表示不继续了});app.use((req, res, next) =>{ req.money -= 2000; next();});app.get('/',(req,res)=>{ res.end('ok'+req.money);});复制代码
最后 localhost:3000 输出ok 3000
中间件实现 express/index.js
...function createApplication(){ let app = (req,res) => { let reqMethod = req.method.toLowerCase(); let {pathname} = url.parse(req.url,true); let index = 0; function next(){ //将回调转换为next形式 if(index === app.routes.length){ res.statusCode = 404; res.end(`Cannot ${reqMethod} ${pathname}`); return; } let {method,path,handler} = app.routes[index++]; if(method === 'middleware'){ //中间件 if(pathname == path || path === '/' || pathname.startsWith(path + '/')){ handler(req,res,next) }else{ next();// 没有迭代到 就执行下一个中间件 } }else{ //路由 if((method === reqMethod || method === all)&& (pathname == path|| pathname === '*')){ handler(req,res); }else{ next(); } } } next(); } app.routes = []; //存储 方法 路径 回调 let methods = ['get', 'post', 'put', 'delete', 'options']; app.use = function(path,handler){ if(typeof handler != 'function'){ handler = path; path = '/' } app.routes.push({ method: 'middleware', path, handler }) } ... return app;}module.exports = createApplication复制代码
有个特殊的地方,比如我们的路径是article/9/zdl
,我们这样写article/:age/:name
=> {age:9,name:zdl}
如何解析这种路径?在express里可以通过req.params获取到
express.js
let express = require('../express');let app = express();app.get('/article/:id/:name', (req,res,next)=> { console.log(req.params); res.end(JSON.stringify(req.params));});app.listen(3000);复制代码
test: localhost:3000/:1/zdl1
express/index.js
...function createApplication(){ let app = (req,res) => { ... function next(){ ... if(method === 'middleware'){ ... }else{ //路由 if(path.params){ // 到路径参数的路由 if(path.test(pathname)){ let params = {} let values = pathname.match(path).slice(1); values.forEach((value,index)=>{ params[path.params[index]] = value }); req.params = params; // 把参数挂载到req上 handler(req,res); }else{ next(); } }else{ ... } } } next(); } ... methods.forEach(method => { app[method] = (path,handler) => { //带路径参数的路由 let params = []; if(path.includes(':')){ //如果路径带有:就把路径转换成正则 path = path.replace(/:([^\/]+)/g,function(){ params.push(arguments[1]); return '([^\/]+)'; }) path = new RegExp(path); path.params = params; } app.routes.push({ method, path, handler }) } }) ... return app;}module.exports = createApplication复制代码
目录:
最后我们完整的index.jslet http = require('http');let url = require('url');function createApplication(){ //若果请求方法,路径一致,则执行回调 let app = (req,res) => { let reqMethod = req.method.toLowerCase(); let {pathname} = url.parse(req.url,true); let index = 0; function next(){ if(index === app.routes.length){ res.statusCode = 404; res.end(`Cannot ${reqMethod} ${pathname}`); return; } let {method,path,handler} = app.routes[index++]; if(method === 'middleware'){ //中间件 if(pathname === path || path == '/' || pathname.startsWith(path + '/')){ handler(req,res,next) }else{ next();// 没有迭代到 就执行下一个中间件 } }else{ //路由 if(path.params){ // 到路径参数的路由 if(path.test(pathname)){ let params = {} let values = pathname.match(path).slice(1); values.forEach((value,index)=>{ params[path.params[index]] = value }); req.params = params; // 把参数挂载到req上 handler(req,res); }else{ next(); } }else{ if((method === reqMethod || method === all)&& (pathname == path|| pathname === '*')){ handler(req,res); }else{ next(); } } } } next(); } app.routes = []; //存储 方法 路径 回调 let methods = ['get', 'post', 'put', 'delete', 'options']; app.use = function(path,handler){ if(typeof handler != 'function'){ handler = path; path = '/' } app.routes.push({ method: 'middleware', path, handler }) } app.all = function (path, handler) { app.routes.push({ method: 'all', path, handler }) } methods.forEach(method => { app[method] = (path,handler) => { //带路径参数的路由 let params = []; if(path.includes(':')){ //如果路径带有:就把路径转换成正则 path = path.replace(/:([^\/]+)/g,function(){ params.push(arguments[1]); return '([^\/]+)'; }) path = new RegExp(path); path.params = params; } app.routes.push({ method, path, handler }) } }) app.listen = (...args) => { let server = http.createServer(app); server.listen(...args); } return app;}module.exports = createApplication复制代码
中间件-1
express内置了很多中间件,把想要的方法已经解析好了,可以直接使用
let express = require('./express');let app = express();app.use(function (req,res,next) { ... next();});app.get('/article', (req,res,next)=> { console.log(req.path,req.query); res.send({ name:'zfpx'});});app.listen(3000);复制代码
实现 app.use
的内容
test: localhost:3000/article?a=1&b=2
app.use(function (req,res,next) { let {path,query} = url.parse(req.url,true); req.path = path; req.query = query; req.hostname = 'XXXX'; //装饰模式 let end = res.end.bind(res); res.send = function(value){ console.log(typeof value); if(typeof value === 'object'){ end(JSON.stringify(value)) }else if(Buffer.isBuffer(value) || typeof value === 'string'){ end(value) }else{ end(value.toString()) } } next();});复制代码
中间件-2
启用一个静态服务
index.js
let express = require('./express');let app = express();app.use(function (req,res,next) { ... next();});app.get('/article', (req,res,next)=> { console.log(req.path,req.query); res.send({ name:'zfpx'});});app.listen(3000);复制代码
use内容
let express = require('express');let app = express();let path = require('path');let fs = require('fs')function static(p){ return (req ,res ,next) => { let newPath = path.join(p, req.path); fs.stat(newPath,(err,stat) => { if(err){ next(); }else{ console.log(stat.isDirectory()); if(stat.isDirectory()){ let p = path.join(newPath,'/index.html'); console.log(p); fs.createReadStream(p).pipe(res) }else{ fs.createReadStream(newPath).pipe(res) } } }) }}复制代码