初窥 NestJs

NestJs 是一个 NodeJs 的服务端框架,同 Spring 与 Java 的关系

一、安装、使用

1. 安装脚手架 nestjs-cli

1
npm install @nestjs/cli -g

2. 创建 Starter

通过命令 nest new project 可以创建一个 Starter 项目:https://github.com/nestjs/typescript-starter.git

然后可以自行修改一些配置,创建属于自己的 Starter,例如将 Controller、Service、Module 分目录存放等等。本文中按照以上目录来进行分类。

3. 路由 Controller

3.1. 创建路由

使用 @Controller() 装饰器定义一个基本的控制器,可以设置参数代表路由路径前缀,默认是 /,例如路由路径前缀:/user 则可以使用装饰器:@Controller('user')

1
2
3
4
5
6
7
8
9
import {Controller, Get} from '@nestjs/common';

@Controller('user')
export UserController {
@Get('info')
getUserInfo(): string {
return 'this action returns user\'s info';
}
}

可以通过 CLI 创建控制器:nest g controller name saveDir,简写:nest g co name saveDir

  • src/controller/name 路径下创建 name.controller.ts
  • src/controller/name 路径下创建 name.controller.spec.ts,若不需要该测试文件,则可以加入 --no-spec 参数来取消。

3.2. 路由匹配规则

按照 Controller 中定义的顺序进行匹配,找到第一个匹配上的路由规则,并交由该函数处理,本质上是将这些路由规则对应的函数绑定到 Controller 对应的原型链上,可在 dist/*.controller.js 文件中查看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
__decorate([
(0, common_1.Get)('test'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], UserController.prototype, "test1", null);
__decorate([
(0, common_1.Get)('test'),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], UserController.prototype, "test2", null);

// decorate 对应函数如下
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};

// 大致解读一下就是用装饰器装饰提供的目标(Controller 对应的原型链,然后使用 key(函数名)进行装饰

在参数相同的情况下,会将 test 路由交给 test1 函数来处理。

3.2.1. 路由通配符

路由支持使用模式匹配,支持的匹配规则有:

  1. *:匹配任何组合字符,包括空字符,例如:a*c,将会匹配路由:acaxxxc
  2. ?:前面一个字符的 0-1 次拓展,例如:a?c,将会匹配路由:cac
  3. +:前面一个字符的 1-n 次拓展,例如:a+c,将会匹配路由:acaacaaac
  4. ():用处就是把括号内的视为一个字符与上面的合用,例如:ac(ac)?:将会匹配路由:acacac
3.2.2. Http 方法装饰器

Nest 为所有标准的 HTTP 方法提供了相应的装饰器:

  1. @Get()、@Post()、@Put()、@Delete():Restful 风格
  2. @All():用来处理所有 HTTP 请求方法的处理程序
  3. @Patch()
  4. @Options()
  5. @Head()
3.2.3. 子域路由

@Controller 装饰器可以接受一个 host 选项,以要求传入请求的 HTTP 主机匹配某个特定值。默认采用 Express 适配器,因为 Fastify 不支持嵌套路由;

1
2
3
4
5
6
7
@Controller({ host: ':account.example.com' })
export class AdminController {
@Get()
getInfo(@HostParam('account') account: string): string {
return `${account}`;
}
}

3.3. Request

3.3.1. 请求参数装饰器

通过装饰器 @Query@Params@Body 来获取三种传参方式

  1. @Query 获取 ?name1=value1&name2=value2 这种类型的传参

    1
    2
    3
    4
    @Get('testRequestQuery')
    testRequestQuery(@Query('version') version: string): string {
    return `${version !== undefined ? version : '未传 version'}`;
    }
  2. @Params 获取 /search/:id 这种类型的传参

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Get('testRequestParams/:username/:id')
    testRequestParams(
    // 推荐一个一个接收,还可以定义参数类型
    @Param('username') username: string,
    @Param('id') id: number,
    ): string
    // 可以通过一个变量接收所有路径参数 @Param() params
    // 然后这样获取参数
    // console.log(params.username, params.id);
    // return `${JSON.stringify(params)}`;
    return `${username}, ${id}`;
    }
  3. @Body 获取 POST 请求体的传参,一般推荐定义 DTO,然后自动注入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Post('testRequestBody')
    testRequestBody(
    @Body() loginUser: LoginUserDto,
    @Req() req: Request,
    ): string {
    // 可以接收 application/x-www-form-urlencoded 与
    // application/json 类型参数
    console.log(req.headers['content-length']);
    return `${JSON.stringify(loginUser)}`;
    }

application/x-www-form-urlencodedapplication/json 的区别,当传输数据参数涉及中文字符较少时,采取第一种格式请求体更短,传输数据参数中文字符较多时,采用第二种格式请求体更短。

因为第一种会将中文进行 urlencoded 编码,第二种有额外的 json 格式的开销。

3.4. Headers

3.4.1 状态码 HttpCode

默认情况下,http 请求正常返回状态码都是 200(除了 POST 请求是 201)可以通过 @HttpCode(code) 装饰器来修改返回状态码。

也可以通过 response 对象自行修改返回码,详见 3.5

3.4.2. 自定义响应头

所设置字符应该符合 Header 规范,否则会报错。

通过 @Header('key', 'value') 装饰器来向响应体中注入自定义 Header,其通过调用 res.header(key, value) 来实现。

3.4.3. 重定向 Redirect

通过 @Redirect('url', 'statusCode') 装饰器来重定向请求,statusCode 默认为 302,这里可以分为静态重定向与动态重定向:

1
2
3
4
5
@Get('redirect')
@Redirect('/', 301)
redirect() {
// 静态重定向,直接在装饰器中使用 @Redirect('/', 301)
}

动态重定向,按照一些请求参数,动态的进行重定向。

1
2
3
4
5
6
7
8
9
@Get('redirectDynamic')
@Redirect()
redirectDynamic(@Query('version') version: string) { // 按照参数动态重定向
// 重定向 redirectDynamic?version=v5 路由
if (version === 'v5') {
return { url: 'http://www.baidu.com', statusCode: 301 };
}
return { url: 'https://www.gov.cn', statusCode: 302 };
}

3.5 Response

如果通过 `Res()` 或 `Response()` 装饰器拿到了 `response` 对象,则需要自行处理响应,否则服务会一直处于挂起状态!!!

Res()Response() 的别名,个人认为作用是:当使用 TypeScript 时,需要标明变量类型,而 response 变量的类型是 Response 与装饰器 Response() 名称冲突,故而起了别名;也或许是仅仅为了简写;

1
2
3
4
5
6
7
8
9
10
11
12
@Get('testHttpCode')
@HttpCode(205)
testResHttpCode(@Res() res: Response) {
// 通过 Res() 装饰器 将 response 对象注入到 res 变量中
// 此时返回码会是 300 而不是 205
res.statusCode = 300;
// 需要自行处理响应,使用 res.json() 或 res.send()
res.send({
user: 'xbai',
name: 'xx',
});
}

4. Providers

Providers 是 Nest 的一个基本概念。许多基本的 Nest 类都可能被视为 provider:service、repository、factory、helper 等等。都可以通过 constructor 注入依赖关系。Provider 只是一个用 @Injectable() 装饰器注释的类。

4.1. 创建 Service

使用装饰器 @Injectable() 来标注一个类作为 Provider,让其由 Controller 使用,

可以通过 CLI 工具来创建 nest g service name saveDir,该命令会自动创建:

  1. name.service.ts:service 文件
  2. name.service.spec.ts:测试文件,通过 --no-spec 参数取消生成该文件
1
2
3
@Injectable
export class xxxService {
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!