Easy to do! Teach you how to create custom Express routing middleware

In the previous two articles, I talked about the interceptor of the handwritten onion model, as well as the regular verification of parameter routing. In this article, I will combine these two contents and write a routing middleware by hand.

  • JS Journey: Unraveling the mystery of koa's onion model-interceptor

  • Do you know the secret of getting js routing parameters? Just three steps!

interceptor

Let's recall the interceptor:

  1. After a request comes, multiple interceptors are executed sequentially
  2. The aspect function has two parameters, the first parameter is the context shared by all interceptors, and the second parameter is the method to execute the next interceptor
  3. When an interceptor executes and reports an error, the following aspect functions are not executed

Implementation code

// 定义一个异步函数,用来实现延时效果
const sleep = (mms = 1000) => {
    
    
  return new Promise((a, b) => {
    
    
    setTimeout(() => a(), mms); // 在mms毫秒后调用resolve函数
  });
};

// 定义一个高阶函数,用来返回一个类似Koa的拦截器
const oap = (number) => {
    
    
  return async (ctx, next) => {
    
    
    console.log("opa " + number); // 打印出"opa " + number
    await sleep(); // 等待一秒
    await next(); // 调用下一个拦截函数,并等待其完成
    console.log("oap " + number); // 打印出"oap " + number
  }
}  

An interceptor function is defined, which receives a global context and a next. It returns an asynchronous function that receives two parameters: ctx and next, representing the context object and the next interception function, respectively. The function of this asynchronous function is to print out "opa " + number, then call the sleep function to wait for one second, then call the next function to execute the next interception function, and wait for it to complete before printing out "oap " + number.

use interceptor

// 创建一个Interceptor类的实例
const interceptor = new Interceptor();

// 添加四个拦截函数
interceptor.use(oap(1)); 
interceptor.use(oap(2));
interceptor.use(oap(3)); 
interceptor.use(oap(4));

// 打印结果
// opa 1
// opa 2
// opa 3
// opa 4
// oap 4
// oap 3
// oap 2
// oap 1

This code is a code that uses Interceptor class and oap function.

Because the interception function is executed according to the onion model, that is, from the outside to the inside, and then from the inside to the outside, forming an onion-like ring structure. Each interception function prints out "opa " + number before calling the next function, then waits for one second, calls the next function to execute the next interception function, and waits for the next function to complete, then prints out "oap " + number

The effect is like the picture below
image.png

Here is just a brief description of the usage. For the detailed code of the interceptor, please see this article:JS Journey: Uncovering the onion model of koa-the mystery of the interceptor

express route verification

const check = (rule,pathname)=>{
    
    
  // 
};
// 打印check函数的调用结果,第一个参数是规则,第二个参数是要检查的路径

console.log(check("/getName/:name", "/getName/zenos"));
// { name: 'zenos' },因为规则和实际路径匹配,并且捕获了zenos作为name参数的值

console.log(check("/getAge/:age", "/getAge/18")); 
// { age: '18' },因为规则和实际路径匹配,并且捕获了18作为age参数的值

console.log(check("/getInfo/:name/:age", "/getInfo/zenos/18")); 
// { name: 'zenos', age: '18' },
// 因为规则和实际路径匹配,并且捕获了zenos作为name参数的值和18作为age参数的值

console.log(check("/getAge/:age", "/getAge/18/12")); 
// null,因为规则和实际路径不匹配,多了一个/12部分

First, a function check is defined, which accepts two parameters, rule and pathname, to verify the incoming parameters.

If the verification is successful, get the parameters in the route. If the verification fails, it returns null; then there is a call to the check function, so that you can better understand the function of the check function.

For details about parameter routing verification, you can see here: Do you know the secret of getting js routing parameters? Just three steps!

route interceptor

// router 函数用于根据指定的请求方法、路由规则和回调函数生成一个中间件
const router = (method, rule, callback) => {
    
    
  // 返回一个中间件函数,该函数接受两个参数:ctx(请求上下文)和 next(执行下一个中间件的函数)
  return (ctx, next) => {
    
    
    const {
    
     req } = ctx;
    const res = check(rule, req.pathname);

    // 检查请求方法和路由规则是否匹配:
    // - res 非空表示路由规则匹配
    // - res.route 为空表示不是一个已经解析过的路由
    // - req.method 与 method 相等,或者 method 为 "*" 表示请求方法匹配
    if (res && !res.route && (req.method === method || method === "*")) {
    
    
      ctx.route = res; // 将解析后的路由信息添加到请求上下文中
      callback(ctx, next); // 执行回调函数
      return;
    }

    // 请求方法或路由规则不匹配时,执行下一个中间件
    next();
  };
};

This code defines a function called router that takes three parameters:

  • method (request method, such as "GET", "POST", etc.)
  • rule (routing rules) and callback (callback function)

The role of the router function is to generate a middleware, which will process the request according to the request method and routing rules. If the request method and routing rules match the provided parameters, the router function will execute the callback function; otherwise, the next middleware will be executed.

The router function can be used to create interceptors that handle different request methods and routing rules, and then can be added to Interceptor's interceptor processing.

Let's take a look at a small demo to experience the usage of the router function

Although it can be used, it looks like a routing middleware. Next, make some changes to make it more like express routing middleware

Retrofit route interceptor

// Route 类用于创建处理不同请求方法的路由处理器
class Route {
    
    
  constructor() {
    
    }

  // get 方法用于创建处理 GET 请求的路由处理器
  get(rule, callback) {
    
    
    return router("GET", rule, callback);
  }

  // post 方法用于创建处理 POST 请求的路由处理器
  post(rule, callback) {
    
    
    return router("POST", rule, callback);
  }

  // all 方法用于创建处理任意请求方法的路由处理器
  all(rule, callback) {
    
    
    return router("*", rule, callback);
  }
}

We define a class called Route. This class provides three methods: get, post and all, which are used to handle requests of GET, POST and any method respectively.

These methods call the router function internally, and pass in the corresponding request method, routing rules and callback function.

Let's look at the specific usage:

const route = new Route();

// 创建 Interceptor 实例
const interceptor = new Interceptor();

// 添加一个处理特定 GET 请求的拦截器
interceptor.use(
  route.get("getInfo/name/:name/age/:age", (ctx, next) => {
    
    
    // 路由逻辑...
    console.log("name: ", ctx.route);
    // 执行下一个中间件
    next();
  })
);

// 添加一个处理特定 POST 请求的拦截器
interceptor.use(
  route.post("postAge/:age", (ctx, next) => {
    
    
    // 路由逻辑...
    console.log("age: ", ctx.route);
    // 执行下一个中间件
    next();
  })
);

Here we create an instance of the Interceptor class and add multiple interceptors by calling the use method. These interceptors use methods of the Route class, such as route.get() and route.post(), to handle specific route requests.

Next, we will test the addition of routing interceptors.

Test route interceptor

interceptor.run({
    
    
  req: {
    
    
    pathname: "getInfo/name/zenos/age/18",
    method: "GET",
  },
});

//name: { params: { name: 'zenos', age: '18' } }

This test case simulates a GET request with the path "getInfo/name/zenos/age/18".

This request matches the first interceptor we added (the one that handles a specific GET request), so the callback function inside this interceptor is executed. In the callback function, we print ctx.route. In this example, ctx.route contains the parameters (name and age) parsed from the route, so we see "name: { params: { name: 'zenos', age: '18' } } in the output ".

interceptor.run({
    
    
  req: {
    
    
    pathname: "getInfo/name/zenos",
    method: "GET",
  },
});

// null

This test case also simulates a GET request with the path "getInfo/name/zenos".

But the path of this request does not match the interceptor we added, so no callback function is executed, and the output is null

interceptor.run({
    
    
  req: {
    
    
    pathname: "postAge/18",
    method: "POST",
  },
});

// age: { params: { age: '18' } }

This test case simulates a POST request with the path "postAge/18"

This request matches the second interceptor we added (the one that handles a specific POST request), so the callback function inside this interceptor is executed. In the callback function, we print ctx.route. In this example, ctx.route contains the parameters (age) parsed from the route, so we see "age: { params: { age: '18' } }" in the output

interceptor.run({
    
    
  req: {
    
    
    pathname: "postAge/18",
    method: "GET",
  },
});
// null

This test case simulates a GET request with path "postAge/18".

This request doesn't match any of the interceptors we added because the path matches the second interceptor, but the request method doesn't (the second interceptor only handles POST requests). So no callback function is executed and the output is null

Summarize

This article describes how to write express routing middleware by hand, and the content involves the code of the other two articles. First, review the code of the interceptor and routing verification, and then write the router function by hand. This function returns a key interceptor. With this interceptor, the handwriting of the middleware is more than half successful.
Generally speaking, the idea is still clear, and a large number of test cases are provided. If you don’t understand, you can leave a message in the comment area. I am active in the Nuggets community.

Links to two other articles

  • JS Journey: Unraveling the mystery of koa's onion model-interceptor

  • Do you know the secret of getting js routing parameters? Just three steps!

Guess you like

Origin blog.csdn.net/qq_34626094/article/details/130516746