This is a gradual Express source learning | small white also can understand the source code the fifth in a series of articles.

Read Lesson5- armed to the teeth in conjunction with this code

Target features and target usage

In this article, we will implement a slightly enhanced version of Express based on the fourth article

  • Req.params is available
  • Provides app.param capabilities

The expected use of Express that this article implements is as follows

const express = require('.. /index.js')
const app = express()

app.get('/user/:userId'.function (req, res, next) {
  res.end(`Welcome, the user.id = ${req.params.userId} and the user.name is ${req.user.name}`)
})

app.param('userId'.function (req, res, next, userId, key) {
  req.user = {
    id: userId,
    name: 'foo'
  }
  next()
})

app.get('/article/:title'.function (req, res, next) {
  res.end(`Welcome, the article's title is ${req.params.title}`)
})

app.listen(3000)
Copy the code

If you are not familiar with the app.param function and req.params, please read the Express documentation before reading this article to understand the use of these two things, otherwise you will not understand this lesson

Source code and explanation

Core implementation: 1. Layer extracts params with path-to-regexp. 2. Inside router. handle, process_params calls the argument handlers in turn

In this class, and last class, the main changes are reflected in two aspects, the other document changes are easy to understand, not explained here

  1. lib/route/layer.js
    1. Inside the match function, we get the params value of the req object
  2. lib/route/index.js
    1. Increase process_params
    2. Add the param function
    3. handle
      1. Copy layer params to req
      2. Call process_params

First let’s look at the layer.match function

Remember where this layer.match was called? Router.handle is called when the request comes in, which means that we have a REQ and the path of the reQ.

Look at the keys on line 52. Remember what the keys are? That’s right, the array that holds the parameter object [{name: ‘userId’}]. Lines 52-61 do this by extracting the parameter values from path and putting them in layer.params if path matches

Router.param is the underlying function that we call when we call app.param

Router.process_params (); router.process_params (); router-process_params ()

There are two levels of recursion in this function called param() and paramCallback(). For both functions, simply put, param is called several times with a few arguments, and the counter is keys.length (line 86 of code), for example

app.get('/user/:userId')
// This has only one argument, userId. Param () is called only once
app.get('/order/:type/:state')
// This takes two arguments, type and state. Param () is called twice
Copy the code

While paramCallback is the current parameter and has several handlers called only a few times, the counter is paramIndex (line 101), and each parameter is cleared, for example

app.param('userId', fn1)
// This userId has only one handler function, and paramCallback is called only once
app.param('userId', fn1, fn2)
// The userId has two handlers, called twice by paramCallback
Copy the code

We can illustrate the recursion of param() and paramCallback() with examples

app.get('/user/:userId', fn)
app.param('userId', handle)
Copy the code

In this case, in chronological order

  • Param call with the parameter userId
  • ParamCallback is called, corresponding to handle
app.get('/order/:type/:state', fn)
app.param('type', handle1, handle2)
app.param('state', handle3)
Copy the code

In this case, in chronological order

  • Param call, corresponding to parameter type
  • ParamCallback calls corresponding to handle1
  • ParamCallback calls corresponding to handle2
  • Param call with parameter state
  • ParamCallback calls corresponding to handle3

Concrete implementation can see the code

This paper summarizes

This article implements an enhanced Express with app.param capabilities, as well as req.params