app.use()

use是使用,到底使用了什么东西呢

app.use() 最简单用法

app.js

  1. const express = require('express')
  2. const app = express()
  3. app.use((request,response,next)=>{
  4. console.log(request.url)
  5. response.write('hi')
  6. next() //如果不next 不会执行下一个use
  7. })
  8. //本来是用response.send()的, 但是不能两次send,所以改用write,这是一个流,所以可以写入多次
  9. app.use((request,response,next)=>{
  10. console.log(2)
  11. response.write('hi')
  12. next()
  13. })
  14. app.use((request,response,next)=>{
  15. response.end() //end表示数据写完了
  16. next() //这个next可以省略 因为在最后一步了。
  17. })
  18. app.listen(3000,()=>{
  19. console.log('正在listen 3000')
  20. })

Express的编程模型

image.png

  • 每一次请求的时候(比如在localhost:3000刷新一次),就会向express服务器发送一次请求
  • 与此同时,express会构造出一个response对象,next函数,连同这一次的请求request,这三个东西都发给app.use()里的function app.use(``function``(req,res,next){})
  • 图中的·启动·意思是:express会在每次请求进来时,做一写准备工作,就是这个启动程序
  • 启动完之后就会进入第一个fn1,然后fn2,然后fn3,如果中间又调用了app.use(),又会把fn插入进去(橙色部分),这样执行下去,直到结束(会执行express的结束程序,一般是一些清理工作)
  • 每一个请求都是上面这些顺序
  • 每一个fn可以做什么(三种常用操作)
    • 读request 读一下用户请求的数据
    • 写response 写一下response里面的内容,比如用response.write()写入, response.end()写入结束
    • 调用next() 表示这个fn的当前任务已经完成

中间件

回到上面的编程模型,fn就是中间件,因为它是被插入到启动和结束中间的物件

其实在上面的代码中

  1. app.use((request,response,next)=>{
  2. console.log(request.url)
  3. response.write('hi')
  4. next() //如果不next 不会执行下一个use
  5. })

这个就是我们的中间件,是我们自己定义的, 还有一些是别人写好的

比如express手脚架中的app.js:
image.png
我们可以改写成

  1. const fn1 =logger('dev')
  2. app.use(fn1)
  3. const fn2 = express.json()
  4. app.use(fn2)

就跟我们的没多大区别了,就看上面的五行代码,其实就是express的编程模型,fn1,fn2这样用下去。
这样用有什么好处呢

优点:模块化

  • 这种模型(express编程模型,fn1,fn2这样)使得每个功能都能通过一个函数实现
  • 然后通过app.use将这个函数整合起来
  • 如果把函数放到文件或npm里,就实现了模块化

写一个独立的模块,任何人访问的时候,都会把他的url打印出来
logger.js

  1. const logger = function (req,res,next){
  2. console.log(req.url)
  3. next()
  4. }

就这么简单就写完了,怎么使用呢:
image.png
访问3000端口
image.png
就打印出来了根目录,但是我根本不用担心logger是怎么实现的,因为它是中间件

改进一下:加一个参数,可以传入一个前缀
logger.js

  1. const logger = (prefix)=>{
  2. return function (req,res,next){
  3. console.log(prefix + req.url)
  4. next()
  5. }
  6. }
  7. module.exports = logger

使用的时候就 app.use(logger(hasson's))
image.png
这就是中间的好处,你只要把它放在中间,然后让它执行,你就根本不用去管它

路由

同样的,express的编程模型使得路由功能变得非常简单。
实现:用户访问三个不同路径是得到三个不同的字符串

  1. app.use((req,res,next)=>{
  2. if(req.path === '/' && res.method === 'get'){
  3. res.send('根目录')
  4. }
  5. next()
  6. })
  7. app.use((req,res,next)=>{
  8. if(req.path === '/aaa'){
  9. res.send('这是aaa')
  10. }
  11. next()
  12. })
  13. app.use((req,res,next)=>{
  14. if(req.path === '/bbb'){
  15. res.send('这是bbb')
  16. }
  17. next()
  18. })

这样可以看到,我们可以把每个模块单独得拎出来,比如说我们可以把/aaa的use写成之前的logger.js一样,所以与其说express是一个web框架,倒不如说它是一个专门做中间件的框架,它使得你的所有功能都可以以中间件的形式插入到app里面。

上面的功能有一些更好用的写法(API糖)

  1. app.use('/aaa',fn)
  2. app.get('/aaa',fn)
  3. app.post('/aaa',fn)
  4. app.route('/xxx').all(f1).get(f2).post(f3)

用这些API改进一下:

  1. app.use('/xxx', (req, res, next) => {
  2. res.send('这是xxx');
  3. next();
  4. });
  5. app.get('/aaa',(req,res,next)=>{
  6. res.send(`这是aaa`)
  7. next()
  8. })
  9. app.route('/bbb')
  10. .all((req,res,next)=>{
  11. res.write(`all`)
  12. next()
  13. }) //不管什么方法 get post put 都会执行回调
  14. .get((req,res,next)=>{
  15. res.write(` bbb`)
  16. res.end()
  17. next()
  18. })
  19. .post()

image.png