express 的中间件

136 阅读9分钟

express 的中间件

  1. 全局中间件:
    • 直接在 index.js 中书写的中间件
    • 使用方式: server.use(以什么开头, 函数)
      • 以什么开头: 可以不写
      • 函数: 你这个中间件要做的事情
  2. 路由级中间件
    • 在路由中书写
    • 使用方式: Router.use(以什么开头, 函数)
      • 以什么开头: 可以不写
      • 函数: 你这个中间件要做的事情
  3. 请求级中间件
    • 在请求的处理中书写, 需要在路由处理函数之前书写
  4. 错误处理中间件(本质上就是全局中间件)
    • 必须放在 监听端口号前一位,中间不能有其他代码

配置全局中间件

新建一个index.txt文件,用来记录请求

修改服务器端文件如下:

    // 0.导入第三方模块
    const express = require('express')
    const Router = require('./router')
    const bodyParser = require('body-parser')
    const fs = require('fs') //node给我们提供的内置模块,导入直接使用即可
    // 1.开启服务
    const server = express()
    // 1.1 解析post参数
    // 解析 application/x-www-form-urlencoded (普通字符串)
    server.use(bodyParser.urlencoded({ extended: false }))
    // 解析 application/json (json字符串)
    server.use(bodyParser.json())
    // 1.1.1 配置全局中间件
    server.use((req, res, next) => {
      /**
       * 需求:在每一次请求这个服务器的时候,记录一下
       * 
       * 解决: 利用node.js 给我们提供的内置模块 fs 来操作
       * 
       * 语法: fs.appendFile('修改的文件路径', `追加到这个文件内的内容,追加完毕后的回调函数(不执行内容也要写一个空函数)`)
       * */ 
      fs.appendFile('./index.txt', `${new Date()} ----- ${req.url}\n`, () => {})
      /**
       * 中间会有第三个参数,next,他是一个函数,如果不调用,那么你的所有请求,都会卡在这个函数中
       * */ 
      next()
    })
    // 1.2开启静态服务
    server.use('/static', express.static('./client'))
    // 1.2配置请求路由
    server.use('/api', Router)
    // 2.监听一个端口号
    server.listen(8080, () => {
      console.log('服务器开启成功')
    })

配置路由级中间件

修改router文件夹下的users.js文件:

    // 导入第三方模块
    const express = require('express')
    const { postUsersInfo, postUsersSetName } = require('../controller/users')
    /**
     * 在当前服务器中,只要是users开头的接口,全都需要登陆过
     * 换句话说,就是请求/users 不管后续的接口是什么,全都需要携带token
     * 所以我们为了减少代码量,好维护代码.我们就需要使用到 路由级别的中间件
     * */ 
    // 创建一张路由分表
    const UsersRouter = express.Router()
    // 挂在路由级别的 中间件
    UsersRouter.use((req, res, next) => {
      // console.log('当前服务器接收到一条请求,需要验证token');
      // 1. 验证token,如果token存在,那么继续向下进行,如果不存在,直接拦截,并且反馈给前端一个信息
      console.log(req.headers.authorization) //假设我们的token就是123456
        if(!req.headers.authorization) {
        res.send({
          code: 0,
          msg: '当前接口需要传递token'
        })
        return
      }
      if(req.headers.authorization !== '123456') {
        res.send({
          code: 0,
          msg: '当前token 过期或伪造'
        })
        return
      }
      next()
    })  
    // 想分表上挂载一些请求处理
    UsersRouter.post('/info', postUsersInfo)
    UsersRouter.post('/setName', postUsersSetName)
    // 导出当前分表
    module.exports = UsersRouter

修改client文件夹下js文件夹下的index.js文件

    function myAjax2() {
      // console.log('向服务端发起一个请求')
      const xhr = new XMLHttpRequest()
      // xhr.open('post', '/users/info')
      xhr.open('post', '/api/users/info')
      xhr.onload = function() {
        console.log(JSON.parse(xhr.responseText))
      }
      //token的值,123456是满足token的值,会请求成功
      xhr.setRequestHeader('authorization', '123456')
      xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
      xhr.send('id=20040216')
    }
    myAjax2()

配置请求级中间件

  • 请求中间件, 一般是用来验证参数的

新建一个middleware文件夹,在其下边新建一个goods.js,该文件专门存储 goods请求相关的中间件函数

    exports._getGoodsList = (req, res, next) => {
      // 验证参数是否符合要求,如果符合那么继续向下,不符合直接拦截
      const {current, pageSize} = req.query;
      console.log(current, pageSize)
      // 1. 非空校验
      if(!current || !pageSize) {
        res.send({
          code: 0,
          msg: '参数数量不对请检查'
        })
        return
      }
      /**
       * 2. 格式校验
       *  isNaN: 会将传递进去的参数,转换为数字类型
       *          并且会验证是否为一个NaN
       *          '123'   ->   123
       *          'abc'   ->   NaN
       * */ 
      if(isNaN(current) || isNaN(pageSize)) {
        res.send({
          code: 0,
          msg: '参数格式不对'
        })
        return
      }
      next()
    }

修改router文件夹下的goods.js文件:

    // 导入第三方模块
    const express = require('express')
    const { getGoodsList, getGoodsInfo } = require('../controller/goods');
    const { _getGoodsList } = require('../middleware/goods');
    // 创建一张路由分表
    const GoodsRouter = express.Router()
    // 想分表上挂载一些请求处理
    //_getGoodsList这个就是引入middleware文件中的变量
    GoodsRouter.get('/list', _getGoodsList, getGoodsList)
    GoodsRouter.get('/info', getGoodsInfo)
    // GoodsRouter.get('/info', testUserName, getGoodsInfo)
    // 导出当前分表
    module.exports = GoodsRouter

配置错误处理中间件

修改服务器端文件如下:

    // 0.导入第三方模块
    const express = require('express')
    const Router = require('./router')
    const bodyParser = require('body-parser')
    const fs = require('fs') //node给我们提供的内置模块,导入直接使用即可
    // 1.开启服务
    const server = express()
    // 1.1 解析post参数
    // 解析 application/x-www-form-urlencoded (普通字符串)
    server.use(bodyParser.urlencoded({ extended: false }))
    // 解析 application/json (json字符串)
    server.use(bodyParser.json())
    // 1.1.1 配置全局中间件
    server.use((req, res, next) => {
      fs.appendFile('./index.txt', `${new Date()} ----- ${req.url}\n`, () => {})
      next()
    })
    // 1.2开启静态服务
    server.use('/static', express.static('./client'))
    // 1.3配置请求路由
    server.use('/api', Router)
    // 全局错误处理中间件
    server.use((error, req, res, next) => {
      // console.log('全局处理中间件')
      /**
       * 约定: (自己和自己约定)
       *    error === 2
       *        参数数量不够
       *    error === 3
       *        参数格式不对
       *    error === 4
       *        token 没有传递
       *    error === 5
       *        token 伪造/过期

       * */ 
      console.log(error)
      if(error === 2) {
        res.send({
          code: 0,
          msg: '参数数量不够'
        })
      }else if(error === 3) {
        res.send({
          code: 0,
          msg: '参数格式不对'
        })
      }else if(error === 4) {
        res.send({
          code: 0,
          msg: 'token 没有传递'
        })
      }else if(error === 5) {
        res.send({
          code: 0,
          msg: 'token 伪造/过期'
        })
      }
    })
    // 2.监听一个端口号
    server.listen(8080, () => {
      console.log('服务器开启成功')
    })

修改middleware文件夹下的goods.js文件:

  • goods.js文件:
    // 该文件专门存储 goods请求相关的中间件函数
    exports._getGoodsList = (req, res, next) => {
      // 验证参数是否符合要求,如果符合那么继续向下,不符合直接拦截
      const {current, pageSize} = req.query;
      console.log(current, pageSize)
      // 1. 非空校验
      if(!current || !pageSize) {
        // res.send({
        //   code: 0,
        //   msg: '参数数量不对请检查'
        // })
        // return
        return next(2)
      }
      /**
       * 2. 格式校验
       *  isNaN: 会将传递进去的参数,转换为数字类型
       *          并且会验证是否为一个NaN
       *          '123'   ->   123
       *          'abc'   ->   NaN
       * */ 
      if(isNaN(current) || isNaN(pageSize)) {
        // res.send({
        //   code: 0,
        //   msg: '参数格式不对'
        // })
        // return
        return next(3)
      }
      next()
    }

    exports._getGoodsInfo = (req, res, next) => {
      // 验证参数是否符合要求,如果符合那么继续向下,不符合直接拦截
      const {goodsId} = req.query;
      console.log(goodsId)
      // 1. 非空校验
      if(!goodsId) {
        // res.send({
        //   code: 0,
        //   msg: '参数数量不对请检查'
        // })
        // return
        return next(2)
      }
      /**
       * 2. 格式校验
       *  isNaN: 会将传递进去的参数,转换为数字类型
       *          并且会验证是否为一个NaN
       *          '123'   ->   123
       *          'abc'   ->   NaN
       * */ 
      if(isNaN(goodsId)) {
        // res.send({
        //   code: 0,
        //   msg: '参数格式不对'
        // })
        // return
        return next(3)
      }
      next()
    }

修改router文件夹下的goods.js文件和users.js文件:

  • goods.js文件:
    // 导入第三方模块
    const express = require('express')
    const { getGoodsList, getGoodsInfo } = require('../controller/goods');
    const { _getGoodsList, _getGoodsInfo } = require('../middleware/goods');
    // 创建一张路由分表
    const GoodsRouter = express.Router()
    // 想分表上挂载一些请求处理
    GoodsRouter.get('/list', _getGoodsList, getGoodsList)
    GoodsRouter.get('/info', _getGoodsInfo, getGoodsInfo)
    // 导出当前分表
    module.exports = GoodsRouter
  • users.js文件:
    // 导入第三方模块
    const express = require('express')
    const { postUsersInfo, postUsersSetName } = require('../controller/users')
    /**
     * 在当前服务器中,只要是users开头的接口,全都需要登陆过
     * 
     * 换句话说,就是请求/users 不管后续的接口是什么,全都需要携带token
     * 
     * 所以我们为了减少代码量,好维护代码.我们就需要使用到 路由级别的中间件
     * */ 
    // 创建一张路由分表
    const UsersRouter = express.Router()
    // 挂在路由级别的 中间件
    UsersRouter.use((req, res, next) => {
      // 1. 验证token,如果token存在,那么继续向下进行,如果不存在,直接拦截,并且反馈给前端一个信息
      console.log(req.headers.authorization) //假设我们的token就是123456
      if(!req.headers.authorization) {
        /**
         * next如果直接调用,是进入到下一个中间件
         * 但是在调用的时候,还可以传递一个参数
         * 如果这个参数传递了值,会跳转到全局错误处理中间件
         * 然后全局错误处理中间件的第一个形参,就是我们调用的时候穿的参数
         * */ 
        return next(4)
      }
      if(req.headers.authorization !== '123456') {
        return next(5)
      }
      next()
    })  
    // 想分表上挂载一些请求处理
    UsersRouter.post('/info', postUsersInfo)
    UsersRouter.post('/setName', postUsersSetName)
    // 导出当前分表
    module.exports = UsersRouter

动态路由

在router文件夹下新建一个cart.js文件, 购物车的接口,将来也是需要登录后才能访问的接口 配置分表cart.js文件的代码:

    // 购物车的接口,将来也是需要登录后才能访问的接口
    // 导入第三方包
    const express = require('express')
    // 创建路由分表
    const CartRouter = express.Router()
    /**
     *  我们目前一直使用的路由, 传参方式, 就是这样
     *  但是有一天, 有一个人, 觉得这样写太丑了, 然后研究出来一个叫做 动态路由的东西
    */
    // 向路由总表上添加路由分表
    // 动态路由写法
    CartRouter.get('/list/:current', (req, res) => {
      /**
         *  如果是动态路由的写法, 参数就不会放在 req.query
         *  而是放在 req.params     (post 与 get 都一样)
        */
      res.send({
        code: 1,
        msg: '需求成功',
        tips: `您请求的列表页为第 ${req.params.current}`
      })
    })
    CartRouter.post('/add/:name', (req, res) => {
      res.send({
        code: 1,
        msg: '需求成功',
        tips: `您请求的列表页为第 ${req.params.name}`
      })
    })
    // 原本写法
    // CartRouter.get('/list', (req, res) => {
    //   res.send({
    //     code: 1,
    //     msg: '需求成功',
    //     tips: `您请求的列表页为第 ${req.query.current}`
    //   })
    // })
    // CartRouter.post('/add', (req, res) => {
    //   res.send({
    //     code: 1,
    //     msg: '需求成功',
    //     tips: `您请求的列表页为第 ${req.body.name}`
    //   })
    // })
    module.exports = CartRouter

将分表cart.js文件引入总表index.js文件

    // 导入第三方模块
    const express = require('express');
    const CartRouter = require('./cart');
    const GoodsRouter = require('./goods');
    const UsersRouter = require('./users');
    // 创建一个总表
    const Router = express.Router();
    // 向路由总表上挂载路由分表
    Router.use('/goods', GoodsRouter);
    Router.use('/users', UsersRouter);
    Router.use('/cart', CartRouter)
    // 导出路由总表
    module.exports = Router;

配置client文件夹下js文件夹下的index.js文件:加入了myAjax5和myAjax6函数

    // 前端的代码, 发送一个请求
    function myAjax1() {
      // console.log('向服务端发起一个请求')
      const xhr = new XMLHttpRequest()
      // xhr.open('get', '/goods/list')
      xhr.open('get', '/api/goods/list?current=1&pageSize=12')

      xhr.onload = function() {
        console.log(JSON.parse(xhr.responseText))
      }
      xhr.send()
    }
    myAjax1()

    function myAjax2() {
      // console.log('向服务端发起一个请求')
      const xhr = new XMLHttpRequest()
      // xhr.open('post', '/users/info')
      xhr.open('post', '/api/users/info')
      xhr.onload = function() {
        console.log(JSON.parse(xhr.responseText))
      }
      xhr.setRequestHeader('authorization', '123456')
      xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
      xhr.send('id=20040216')
    }
    myAjax2()

    function myAjax3() {
      // console.log('向服务端发起一个请求')
      const xhr = new XMLHttpRequest()
      // xhr.open('get', '/goods/list')
      xhr.open('get', '/api/goods/info?goodsId=10086')

      xhr.onload = function() {
        console.log(JSON.parse(xhr.responseText))
      }
      xhr.send()
    }
    myAjax3()

    function myAjax4() {
      // console.log('向服务端发起一个请求')
      const xhr = new XMLHttpRequest()
      // xhr.open('post', '/users/info')
      xhr.open('post', '/api/users/setName')
      xhr.onload = function() {
        console.log(JSON.parse(xhr.responseText))
      }
      xhr.setRequestHeader('authorization', '123456')
      xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
      xhr.send('id=20040216')
    }
    myAjax4()

    function myAjax5() {
      // console.log('向服务端发起一个请求')
      const xhr = new XMLHttpRequest()
      // xhr.open('get', '/api/cart/list?current=10086')
      xhr.open('get', '/api/cart/list/10086')
      xhr.onload = function() {
        console.log(JSON.parse(xhr.responseText))
      }
      xhr.send()
    }
    myAjax5()

    function myAjax6() {
      const xhr = new XMLHttpRequest()
      // xhr.open('post', '/api/cart/add')
      xhr.open('post', '/api/cart/add/老北京卤煮')
      xhr.onload = function() {
        console.log(JSON.parse(xhr.responseText))
      }
      // xhr.setRequestHeader('content-type', 'application/x-www-form-urlencoded')
      // xhr.send('name=老北京卤煮')
      xhr.send()
    }
    myAjax6()