Classification of middleware
Express officially divides common middleware usage into five categories:
Application-level middleware
Route-level middleware
Error-level middleware
Express built-in middleware
Third-party middleware
Application level middleware
Via
app.use()
orapp.get()
orapp.post()
, Middleware bound to the app instance, called application-level middleware. (That is, the example in Section 4.4.)1
2
3
4
5
6app.use((req, res, next) => {
next()
})
app.get('/', mw1, (req, res) => {
res.send('Home page.')
})
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
9const 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
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
16app.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
5const 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 vianew
constructs orextends
, it counts as an instance of the latter.1
2
3
4
5const errorM1 = (error, req, res, next) => {
if(error instanceof CustomError){
res.status(400).json({error:error.message});
}
}
Registration error middleware
Registered globally in
app.use()
, beforeapp.listen()
.
Express built-in middleware
Since Express version 4.16.0, Express has built-in 3 commonly used middleware to improve development efficiency and experience:
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.
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.
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
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:
Run
npm install middleware package_name
to install the middlewareUse require to import middleware (requires path)
Call
app.use()
to register and use middleware
Custom middleware case
Manually emulate a middleware like express.urlencoded to parse the form data POSTed to the server. step:
Define middleware
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
4let 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.
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
3req.on('end', () => {
console.log(str)
})
Use the querystring module to parse the request body data
1
2
3
4
5const qs = require('querystring')
req.on('end', () => {
const body = qs.parse(str)
next()
})
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
6const qs = require('querystring')
req.on('end', () => {
const body = qs.parse(str)
req.body = body
next()
})
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 module1
2const myBodyParser = require('custom-body-parser')
app.use(myBodyParser) //call the module