中间件

Middleware is a function, which is called before route handler. Middleware functions have access to request and response objects, so they can modify them. They can also be something like a barrier - if middleware function does not call next(), the request will never be handled by route handler.

中间件是在路由处理器工作之前被调用的函数。中间件函数可以访问requestresponse对象,所以中间件函数也就可以修改requestresponse对象。中间件也可以被看作是挡在中间的屏障--如果中间件没有调用next(),路由处理器也就没法处理request

图片标题

The simplest example:

请看一个简单的例子:

import { Middleware, NestMiddleware } from '@nestjs/common';

@Middleware()
export class LoggingMiddleware implements NestMiddleware {
    resolve(): (req, res, next) => void {
        return (req, res, next) => {
            console.log('Request...');
            next();
        }
    }
}

Let's build a dummy authorization middleware (for explanation purposes - just by username). We will use X-Access-Token HTTP header to provide username (weird idea, but it doesn't matter here).

让我们构建一个虚拟授权中间件(用于解释目的——只需通过用户名)。我们将用x-access-token HTTP header提供用户名(奇怪的想法,但是这不重要)。

import { HttpException } from '@nestjs/core';
import { Middleware, NestMiddleware } from '@nestjs/common';
import { UsersService } from './users.service';

@Middleware()
export class AuthMiddleware implements NestMiddleware {
    constructor(private usersService: UsersService) {}

    resolve(): (req, res, next) => void {
        return async (req, res, next) => {
            const userName = req.headers["x-access-token"];
            const users = await this.usersService.getAllUsers();

            const user = users.find((user) => user.name === userName);
            if (!user) {
                throw new HttpException('User not found.', 401);
            }
            req.user = user;
            next();
        }
    }
}

Some facts about middlewares:

  • you should use @Middleware() annotation to tell Nest, that this class is a middleware,
  • you can use NestMiddleware interface, which forces on you to implement resolve() method,
  • middlewares (same as components) can inject dependencies through their constructor (dependencies have to be a part of module),
  • middlewares must have resolve() method, which must return another function (higher order function). Why? Because there is a lot of third-party, ready to use Express Middlewares (and more), which you could simply - thanks to this solution - use in Nest.

Okey, we already have prepared middleware, but we are not using it anywhere. Let's set it up:

关于中间件的一些实际情况:

  • 使用@Middleware()注释告诉Nest,这个类是一个中间件。
  • 可以使用NestMiddleware接口,这会迫使你执行resolve()方法。
  • 中间件(如组件一样)可以通过构造函数注入依赖(依赖必须是模块的一部分)。
  • 中间件必须有resolve()方法,该方法必须返回另一个函数(高阶函数)。这是为什么呢?因为这样的话,你可以轻松使用将要使用Express中间件的第三方插件。 中间件准备就绪,先设置好,待用。
import { Module, MiddlewaresConsumer } from '@nestjs/common';

@Module({
    controllers: [ UsersController ],
    components: [ UsersService ],
    exports: [ UsersService ],
})
export class UsersModule {
    configure(consumer: MiddlewaresConsumer) {
        consumer.apply(AuthMiddleware).forRoutes(UsersController);
    }
}

As shown, Modules can have additional method - configure(). This method receives as a parameter MiddlewaresConsumer, an object, which helps us to configure middlewares.

如上所示,模块还有其他方法-configure()。该方法接收MiddlewaresConsumer对象作为一个参数,该对象可以帮助我们设置中间件。

This object has apply() method, which receives infinite count of middlewares (method uses spread operator, so it is possible to pass multiple classes separated by comma). apply() method returns object, which has two methods:

  • forRoutes() - we use this method to pass infinite count of Controllers or objects (with path and method properties) separated by comma,
  • with() - we use this method to pass custom arguments to resolve() method of middleware.

该对象有apply()方法,它可以接收无数个中间件(该方法使用扩展运算符,所以可以传递无数个用逗号隔开的类)。apply()方法可以通过以下两种方式返回对象:

  • forRoutes()使用此方法传递无数个用逗号隔开的控制器或对象(有路径和方法属性)。
  • with()使用此方法将自定义参数传递给中间件的apply()方法。

    运行原理

When you pass UsersController in forRoutes method, Nest will setup middleware for each route in controller:

使用 forRoutes 方法传递 UsersController 时,Nest会为控制器中的每个路由设置中间件。

GET: users
GET: users/:id 
POST: users

But it is also possible to directly define for which path middleware should be used, just like that:

但是我们也可以通过以下方式直接指定中间件将要使用的路由。

consumer.apply(AuthMiddleware).forRoutes({ 
    path: '*', method: RequestMethod.ALL 
});

传递参数(给中间件)

Sometimes the behaviour of the middleware depends on custom values - e.g. array of users roles. We can pass additional arguments to middleware using with() method.

有时中间件的行为取决于自定义值-如用户角色数组。我们可以通过with()方法向中间件传递附加参数。

示例:

const roles = ['admin', 'user'];
const options = {};
consumer.apply(AuthMiddleware)
    .with(roles, options)
    .forRoutes(UsersController);
@Middleware()
export class AuthMiddleware implements NestMiddleware 
    resolve(roles: string[], options: object) {
        return async (req, res, next) => {
            console.log(roles); // ['admin', 'user'] in this case
            next();
        }
    }
}

You can simply chain apply() calls:

你可以简单地调用 apply()链:

consumer.apply(AuthMiddleware, PassportMidleware)
       .forRoutes(UsersController, OrdersController, ClientController);
       .apply(...)
       .forRoutes(...);

调用顺序

Middlewares are called in the same order as they are placed in array. Middlewares configured in sub-module are called after the parent module configuration.

中间件是放在数组中的,所以他们被同时调用。sub-module(子模块)中配置的的中间件会在父模块配置的中间件之后被调用。。

results matching ""

    No results matching ""