Middleware classification

  1. Classification of middleware

    Express officially divides common middleware usage into five categories:

    1. Application-level middleware

    2. Route-level middleware

    3. Error-level middleware

    4. Express built-in middleware

    5. Third-party middleware


  2. Application level middleware

    Via app.use() or app.get() or app.post() , Middleware bound to the app instance, called application-level middleware. (That is, the example in Section 4.4.)

    1
    2
    3
    4
    5
    6
    app.use((req, res, next) => {
    next()
    })
    app.get('/', mw1, (req, res) => {
    res.send('Home page.')
    })


  1. Route level middleware

    Middleware bound to an express.Router() instance is called a route-level middleware. Its usage is no different from application-level middleware. The difference is that the application-level middleware is bound to the app instance, and the route-level middleware is bound to the router instance:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    const app = express()
    const router = express.Router()

    router.use(function(req, res, next){ //Route-level middleware
    console.log('Time: ', Date.now())
    next()
    })

    app.use('/', router) //register route and add prefix


  1. Error level middleware

    The role of the error level middleware: It is specially used to capture the exception errors that occur in the entire project, thereby preventing the problem of the project from crashing abnormally. When an error occurs in the code, it can be directly caught by the error middleware (the code jumps to the error middleware), similar to catch(). If the error middleware is not written, the project will crash when an error occurs and cannot continue to run.

    In the function processing function of the error level middleware, must have 4 formal parameters, which are (err, req, res, next) from front to back.


    Error level middleware must be registered after all routes.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    app.get('/', (req, res) => { //Route
    throw new Error('Error in server!') //Throw a custom error
    res.send('Home page.')
    })
    const middleware1 = (req, res) => { //Route
    if token not exist {
    next(new Error('error')) // use next() to throw an error
    return;
    }
    next()
    })

    app.use(function(err, req, res, next){ //Error level middleware
    console.log('Error happened: ' + err.message) //Print error message on the server
    res.send('Error! ' + err.message) //Response error message to client
    })

    There are generally multiple error middleware, and each error middleware is responsible for one type of error. When calling the next error middleware, the error needs to be passed on in the form of next() parameter. However, even with multiple error middleware, there is no guarantee that all errors are caught 100%. So check if the error was passed to the last error middleware (the last error middleware doesn’t handle errors, it’s the bottom line). If passed, it should return a 500 status code (bad choice, but only write a 500 status code), and log the error for later fixes.


    How to handle errors in the error handler, determine the statue code, and extract the information from the error object

    Errors are not written by themselves

    1
    2
    3
    4
    5
    const errorM1 = (error, req, res, next) => { //error usually passes an object, although a string can be passed.
    if(error.type === 'validationError'){
    res.status(400).json({error:error.message});
    }
    }

    Errors are self-written and detected with instanceof. This method can detect whether the former is an instance of the latter. If the former is inherited by the latter via new constructs or extends, it counts as an instance of the latter.

    1
    2
    3
    4
    5
    const errorM1 = (error, req, res, next) => {
    if(error instanceof CustomError){
    res.status(400).json({error:error.message});
    }
    }

    Registration error middleware

    Registered globally in app.use(), before app.listen().


  2. Express built-in middleware

    Since Express version 4.16.0, Express has built-in 3 commonly used middleware to improve development efficiency and experience:

    1. express.static The built-in middleware for fast hosting of static resources (detailed in Section 4.2), such as HTML files, images, CSS styles, etc., has no compatibility.


    2. express.json parses the request body data in JSON format (with compatibility, only available in 4.16.0+ version). The request body data can be generated in postman, select body-raw-rightmost drop-down menu and select JSON. Object in JSON format, attribute name must be added with " ".

      The req.body property can be used in the server to receive the request body data sent by the client. By default, req.body equals undefined if no middleware for parsing form data is configured.

      The parsed data will be mounted in req.body and can be accessed later.

      1
      app.use(express.json()) //Global middleware. There is no need to add parameters/attributes in (), because it is a global analysis.

    3. express.urlencoded parses request body data in URL-encoded format (compatibility, only available in 4.16.0+ versions).

      Basically the same as express.json usage. Select body-(x-www-form-urlencoded) on postman.

      1
      app.use(express.urlencoded({ extended:false })) //Global middleware, fixed writing


  1. Third-party middleware

    It is not an official built-in Express, but a middleware developed by a third party. Before express 4.16.0, the body-parser third-party middleware was often used to parse request body data. Third-party middleware needs to be downloaded and configured to be used. Steps for usage:

    1. Run npm install middleware package_name to install the middleware

    2. Use require to import middleware (requires path)

    3. Call app.use() to register and use middleware


  2. Custom middleware case

    Manually emulate a middleware like express.urlencoded to parse the form data POSTed to the server. step:

    1. Define middleware

    2. Listen to the data event of req (as long as the data event is triggered, it proves that data is sent to the server)

      In the middleware, you need to listen to the data event of the req object to get the data sent by the client to the server (use .on to listen).

      If the amount of data is relatively large and cannot be sent all at once, the client will cut the data and send it to the server in batches. Therefore, the data event may be triggered multiple times. Each time the data event is triggered, the obtained data is only a part of the complete data, and the received data needs to be spliced manually.

      1
      2
      3
      4
      let str = '' //Used to store the request body data sent by the client
      req.on('data', (chunk) => { //.on method binds events.
      str += chunk //splicing request body data
      }) //Next() is not written here because it is not a separate middleware, but it is placed inside the same middleware with the end event below.

    3. Listen to the end event of req (end triggering proves that the data has been sent and the server has completely received the form data)

      1
      2
      3
      req.on('end', () => {
      console.log(str)
      })

    4. Use the querystring module to parse the request body data

      1
      2
      3
      4
      5
      const qs = require('querystring')
      req.on('end', () => {
      const body = qs.parse(str)
      next()
      })

    5. Mount the parsed data object as req.body (because express uses req.body to receive the request body data from the client, here we need to replace the original data with data in a new format)

      The upstream middleware and downstream middleware and routes share the same req and res. Therefore, we can mount the parsed data as a custom attribute of req and name it req.body for downstream use.

      1
      2
      3
      4
      5
      6
      const qs = require('querystring')
      req.on('end', () => {
      const body = qs.parse(str)
      req.body = body
      next()
      })

    6. Encapsulate custom middleware into modules

      1
      2
      3
      4
      //custom-body-parser.js module
      const qs = require('querystring')
      function bodyParser(req, res, next){/* Omit other code, in fact, put the previous steps into a middleware */}
      module.exports = bodyParser //encapsulated into a module
      1
      2
      const myBodyParser = require('custom-body-parser')
      app.use(myBodyParser) //call the module

Share