Node: Express的中间件的使用
一、简介
Express框架为开发者提供了一个中间件功能,这个中间件在服务的请求-响应过程提供一个可以修改内容的机会。中间件的完整结构是这样的,它必须是一个函数,可以访问的参数有错误对象err、请求对象req、响应对象res、以及另一个中间件next函数。next函数的作用是可以将当前中间件的控制权进行转交向下传递。大致结构如下:
// 中间件完整的结构 // 1、是一个函数 // 2、可选参数:err、req、res、 next, 注意:err/req/res均为对象、 next为函数,也是下一个中间件 function middleware(err, req, res, next) { //中间件负责的工作可以包括以下几种: //1、异常处理 //2、功能函数,处理一下业务功能,然后转交控制权 //3、响应请求----->结束响应----->当做路由的处理函数 }
二、应用
对中间件结构有了初步了解后,现在可以简单使用一下中间件这个功能。例如,对所有发送的HTTP请求,验证其参数的合法性。如果验证成功,则继续后续的请求操作。代码如下:
//导入express框架 const express = require('express'); //创建express服务实例 const app = express(); //定义一个验证参数的中间件 function valid_name_middleware(req, res, next) { let {name} = req.query; if (!name || !name.length) { res.json({ message:'缺少name参数' }) }else{ next(); //转交控制权 } } //1、这里首先会验证所有的HTTP请求 app.all('*', valid_name_middleware); //2、如果合法,接着才会进行get请求 app.get('/demo', (req, res) => { res.json({ message: 'response success for demo' }) }) //监听 app.listen(3000, ()=>{ console.log('服务器启动了!'); });
三、类型
1、应用级别的中间件
在全局作用域上起作用,处于最顶层结构上,在app实例化时就立马注册使用它。使用app.use----api去加载。
//导入express框架 const express = require('express'); //创建express服务实例 const app = express(); //自定义一个logger中间件 function logger_middleware(req, res, next) { console.log('请求来了!'); next(); } //注册到app全局的级别上 app.use(logger_middleware); //监听 app.listen(3000, ()=>{ console.log('服务器启动了!'); });
//终端结果如下: [nodemon] restarting due to changes... [nodemon] starting `node src/app.js` 服务器启动了! 请求来了!
2、系统内置的中间件
express.static是express内置的中间件之一,基于serve-static,它负责处理应用中的静态资源的。还有express.json()、express.static()、express.Router()、express.urlencoded()。
//导入express框架 const express = require('express'); //创建express服务实例 const app = express(); //加载一个 public 的中间件 //express.static(root, [options]):参数 root 指提供静态资源的根目录路径 //可选的 options 参数可参考官方文档:http://www.expressjs.com.cn/4x/api.html#express.static app.use(express.static('public',{ })) //监听 app.listen(3000, ()=>{ console.log('服务器启动了!'); });
3、第三方提供中间件
cookie-parser和multiparty都是第三方提供的中间件,cookie-parser是用来解析cookie的,multiparty则是用来进行表单或图片等资源上传的。
//导入express框架 const express = require('express'); //创建express服务实例 const app = express(); //加载第三方解析cookie的中间件:cookie-parser //先安装"cookie-parser": npm install cookie-parser -D //然后使用use加载 cookie 解析中间件 var cookieParser = require('cookie-parser'); app.use(cookieParser()); //加载第三方上传资源的中间件:multiparty //先安装"multiparty": npm install multiparty -D const multiparty = require('multiparty'); app.post('/upload', (req, res) => { let form = new multiparty.Form() form.uploadDir = './images';//指定图片的文件夹 form.parse(req,function(err,fields,files){ //获取提交的数据以及图片上传成功返回的图片信息 field是表单数据,files为图片信息 }) }) //监听 app.listen(3000, ()=>{ console.log('服务器启动了!'); });
4、路由级别的中间件
在前面介绍路由时,对路由进行拆分后统一到应用顶层进行注册时,用到的方式就是将路由作为中间件使用。也是使用use()函数。
//导入express框架 const express = require('express'); //创建express服务实例 const app = express(); //导入路由 const userRouter = require('./user.router') //注册路由,将路由当做中间件,放在应用级别上 app.use(userRouter); //也可以给userRouter配一个父级别的路由, 相当于起了一个命令空间的作用 app.use('/user', userRouter); //此时,访问"http://127.0.0.1:3000/information" 和 "http://127.0.0.1:3000/user/information"都可以请求成功 //监听 app.listen(3000, ()=>{ console.log('服务器启动了!'); });
上面的路由作为中间件放在了应用级别上,此时还可以在路由内部定义中间件,需要修改user.router.js文件如下:
//导入express框架 const express = require('express'); //定义一个路由 const router = express.Router(); /* //第一个场景:在路由内部定义一个中间件 router.use(function(req, res, next){ console.log('logger from inner router'); next(); }) //发起get请求 router.get('/information', (req, res) => { res.json({ message:"from router user !" }) }) */ //第二个场景:针对单个路由,支持中间件栈,也就是一个数组,依次执行 function valid_login_parms_middleware(req, res, next) { let{username, password} = req.query; if (!username || !password) { res.json({ message:"参数校验失败!" }) } else{ //将内容传递给next req.result = { username, password } next(); //转交控制权 } } //发起get请求 //第2个参数是数组,可以放很多个中间件,依次执行 router.get('/login', [valid_login_parms_middleware], (req, res) => { let {result} = req; res.json({ result, message:"from router user ! login success!" }) }) //将路由暴露出来 module.exports = router;
第一种场景Postman运行的结果还是一样,不过在get请求响应之前,路由内部定义的中间件会先执行,然后由next函数转交控制权给get请求。
//终端结果如下: [nodemon] restarting due to changes... [nodemon] starting `node src/app.js` 服务器启动了! logger from inner router
第二个场景Postman运行的结果如下:
5、异常处理的中间件
中间件的参数中有一个err对象可以访问,因此,开发者可以定制一个处理异常的中间件,将请求错误的日志可视化展现出来,提高开发效率。注意,处理异常的中间件必须注册到所有路由的底部,这样才能监听异常。
//导入express框架 const express = require('express'); //创建express服务实例 const app = express(); //发送一个get请求,抛出异常 app.get('/user', (req, res) => { throw new Error('测试接口异常!') }) //监听 app.listen(3000, ()=>{ console.log('服务器启动了!'); });
这个方式抛出的这个日志很复杂,非常不利于查找原因,截图如下:
此时修改这个文件,定制一个异常处理的中间件,此时抛出来的日志相当明显,非常清新,修改和截图如下:
//导入express框架 const express = require('express'); //创建express服务实例 const app = express(); //发送一个get请求 app.get('/user', (req, res) => { throw new Error('测试接口异常!') }) //定义一个异常处理的中间件 function error_handle_middleware(err, req, res, next) { let {message} = err; if (err) { res.status(500) res.json({ message:`${message || '服务器异常'}` }) }else{ // } } //err为空时,这个是最后处理不了一般的错误时,报404。 function not_found_handler(err, res, next) { res.json({ message:'api不存在!' }) } app.use(error_handle_middleware); app.use(not_found_handler); //监听 app.listen(3000, ()=>{ console.log('服务器启动了!'); });