【Nest系列】5. Nest 核心概念:控制器

简介: 上篇文章《Nest 核心概念:模块》中,我们介绍了 Nest 的模块,它是组织 Nest 应用的基本结构。Nest 应用接收请求,交给控制器处理请求,控制器中又调用服务,完成具体的业务逻辑,最后将结果响应给客户端。本文会详细介绍控制器的用法,其实就是通过各类装饰器处理不同的请求和传参。

前言

上篇文章《Nest 核心概念:模块》中,我们介绍了 Nest 的模块,它是组织 Nest 应用的基本结构。Nest 应用接收请求,交给控制器处理请求,控制器中又调用服务,完成具体的业务逻辑,最后将结果响应给客户端。

本文会详细介绍控制器的用法,其实就是通过各类装饰器处理不同的请求和传参。

控制器

控制器负责处理传入的请求,并向客户端返回响应。所发挥的作用就类似于 Express 或者 Koa 中的路由中间件。

image-20230119151105051

代码实现上,控制器就是使用 @Controller 装饰的类。看下它的类型声明:

export declare function Controller(prefix: string | string[]): ClassDecorator;

Controller 是一个装饰器工厂,接收一个 prefix 前缀参数,类型是字符串或者字符串数组,表示模块的路由的公共前缀,返回一个类装饰器 ClassDecorator

接收请求

Nest 为所有标准的 HTTP 方法提供了相应的装饰器:@Get()@Post()@Put()@Delete()@Patch()@Options()、以及 @Head()。还提供了一个特殊的装饰器@All() ,用于定义一个用于处理所有 HTTP 请求方法的处理程序。这些装饰器都从 @nestjs/common 包中导出。

下面是使用 nest generate resource user 命令生成的一个 CRUD 的例子:

import {
   
   
  Controller,Get,Post,Body,Patch,Param,Delete,
} from '@nestjs/common';
import {
   
    UserService } from './user.service';
import {
   
    CreateUserDto } from './dto/create-user.dto';
import {
   
    UpdateUserDto } from './dto/update-user.dto';

// 将类声明为控制器,接收的参数可作为公共路由前缀,可和方法装饰器的参数进行拼接
@Controller('user')
export class UserController {
   
   
  constructor(private readonly userService: UserService) {
   
   }

  // 处理 Get 请求
  @Get()
  findAll() {
   
   
    return this.userService.findAll();
  }

  // 处理 Post 请求
  @Post()
  create(@Body() createUserDto: CreateUserDto) {
   
   
    return this.userService.create(createUserDto);
  }

  // 处理 Patch 请求
  @Patch(':id')
  update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
   
   
    return this.userService.update(+id, updateUserDto);
  }

  // 处理 Delete 请求
  @Delete(':id')
  remove(@Param('id') id: string) {
   
   
    return this.userService.remove(+id);
  }
}

示例中还有一些其他装饰器,下面接着讲。

处理请求参数

客户端向服务端传参有三种方式:

  • query 参数:通过 URL 的查询参数传递,形如 /users/list?page=1
  • param 参数:参数放在 URL 的 path 部分传递,形如 /users/1 , 1 可以表示 id 参数为 1
  • body 参数:将数据放到请求体中传递

其中服务端要接收到 param 参数,需要预先在定义路由和路径时使用元数据定义好,这也叫动态路由,类似于 Koa 中的动态路由:

app.get('/user/:id', async (ctx, next) => {
   
   })

或者前端路由中的动态路由,比如 vue-router 的一段配置:

const route = [
    {
   
   
        path: '/user/:id',
        component: User
    }
]

Nest 控制器定义动态路由的方式如下:

@Controller(‘user’)
class UserController {
   
   
    @Get(':id')
    findOne(@Param('id') id: string) {
   
   
      return this.userService.findOne(+id);
    }
}

拼接过后的动态路由就是 /user/:id

Nest 提供了 QueryParamBody 三种装饰器,来获取这三类参数。它们属于参数装饰器,在方法的参数中使用。

  @Get()
  findAll(@Query() query) {
   
   
    console.log(query);
    return this.userService.findAll();
  }

  @Get(':id')
  findOne(@Param() param) {
   
   
    console.log(param);
    return this.userService.findOne(+param.id);
  }

使用 Postman 分别量给请求到 Nest:

Get /user?page=1
Get /user/10

命令行窗口中会打印出:

image-20230119173446728

还可以通过参数装饰器的参数,直接获取具体的参数:

 @Get()
  findAll(@Query('page') page) {
   
   
    console.log(page);
    return this.userService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id) {
   
   
    console.log(id);
    return this.userService.findOne(+param.id);
  }

命令行窗口中打印出:

image-20230119173654507

对于 body 参数,可以使用 @Body() 装饰器:

@Post()
  create(@Body() createUserDto: CreateUserDto) {
   
   
    console.log(createUserDto);
    return this.userService.create(createUserDto);
}

使用 Postman 发送一个 Post 请求:

image-20230119174016308

命令行窗口中打印:

image-20230119174123716

状态码

针对常用的 HTTP 请求,比如 Get,Post,Put,Patch,Delete,只有 Post 的响应状态码是 201,其他的都是 200。这是 Nest 默认处理的。实际场景中状态码通常需要结合具体的请求处理结果进行设置,比如用户的 token 失效了,此时要设置 401,可以使用 @HttpCode() 装饰器:

import {
   
    HttpCode } from '@nestjs/common';

@Get()
@HttpCode(401)
getList() {
   
   
  return '401';
}

image-20230120092453172

@Headers() 和 @Header() 装饰器

@Headers() 是参数装饰器,用于在一个请求处理中获取需要的请求头信息。

@Header() 是方法装饰器,用于在一个请求中设置响应头信息。

import {
   
    Controller, Post, Headers, Header } from '@nestjs/common';
import {
   
    UserService } from './user.service';


@Controller('user')
export class UserController {
   
   
  constructor(private readonly userService: UserService) {
   
   }

  @Post()
  // 设置响应头
  @Header('user', 'kw')
  // 获取请求头
  create(@Headers('Content-Type') contentType: string) {
   
   
    console.log(contentType);
    return 'nest controller'
  }
}

在 Postman 中发送一个 Post 请求,传递的是 JSON 格式的数据,则 Postman 自动处理 Content-Typeapplication/json

image-20230119201107488

服务端打印的信息是:

image-20230119200926788

Postman 得到的响应头中有我们在控制器中自定义的 user 头:

image-20230119201153398

使用底层框架的原生 API

虽然 Nest 提供了一些处理请求相关的装饰器,但是还不够细致。有时候需要获取更详细的客户端请求的细节,Nest 也考虑到这一点,提供了参数装饰器 @Request(),别名 @Req,表示底层框架的原生请求对象,与之对应的还有 @Response()/ @Req() 装饰器,表示底层框架的原生响应对象。如果你熟悉 Express,就可以在 Nest 中使用 Express 的 reqres 对象了。比如在一个请求中,从请求头中解析 Authorization Header,除了使用 @Headers() 装饰器以外,还可以使用 @Req() 装饰器:

import {
   
    Controller, Get, Req } from '@nestjs/common';
import {
   
    AppService } from './app.service';
// 导入 Request ,作为 req 对象的类型使用
import {
   
    Request } from 'express';

@Controller()
export class AppController {
   
   

  @Get()
  getList(@Req() req: Request) {
   
   
    const authorization = req.headers.authorization;

    return authorization;
  }
}

使用 Postman 发送一个带 Authorization Header 的请求,控制器可以正确解析出 Header 并响应给客户端:

image-20230120105152019

两种响应方式

控制器响应数据给客户端有两种方式。一种是默认的标准方式,也就是在控制器的成员方法中直接 return。此时 Nest 框架会自动处理返回的数据。当返回一个 JavaScript 对象或者数组时,Nest 会自动序列化为 JSON 对象。当返回一个 JavaScript 基本类型(例如string、number、boolean)时, Nest 将只发送值,而不尝试序列化它。

还有一种响应方式就是使用底层框架的响应对象,例如使用 Express 的原生 res 对象,先通过 @Res() 装饰器取得原生响应对象,导入 express 包中的 Response 作为响应对象的类型:

import {
   
    @Res } from '@nestjs/common';
import {
   
    Response } from 'express';

@Controller()
export class AppController {
   
   

  @Get()
  getHello(@Res() res: Response) {
   
   
    res.send('hello')
  }
}

这种方式能提供很大的灵活性,但也存在缺点,一是代码脱离了 Nest 的抽象,去依赖了底层的框架,Express 和 Fastify 在做响应时可能具有不同的 API。如果之后将 Express 更换为 Fastify,可能需要回过头来修改代码。二是如果 Nest 检测到在方法中注入了 @Req(),将不能再使用标准方式直接 return 响应,必须通过 res 去设置。如果依然想使用 Nest 框架的能力,可以给 @Req() 传入一个元数据对象进行设置:

import {
   
    @Res } from '@nestjs/common';
import {
   
    Response } from 'express';

@Controller()
export class AppController {
   
   

  @Get()
  getHello(@Res({
   
    passthrough: true }) res: Response) {
   
   
    res.append('User', 'Kunwu')
    return 'hello'
  }
}

image-20230120111437399

重定向

要做请求的重定向,可以使用 @Redirect() 装饰器,它接收两个可选参数,urlstatusCode 用于设置重定向的 URL 和 状态码。重定向状态码默认为 302。或者使用上面介绍的使用底层框架的 res 对象(res.redirect())。

使用装饰器重定向:

@Get('docs')
@Redirect('https://docs.nestjs.com')
getDocs() {
   
   
  return;
}

如果需要动态进行重定向,在使用了 @Redirect() 之后,控制器方法返回装饰器的参数对象:

@Get('docs')
@Redirect()
getDocs(@Query('version') version) {
   
   
  if (version && version === '8') {
   
   
    return {
   
    
        url: 'https://docs.nestjs.com/v8/' 
    };
  }
}

最后

关于控制器,本文介绍了用到比较多的特性,包括:

  • 接收特定请求
  • 处理请求参数
  • 设置状态码,解析请求头,设置响应头
  • 使用底层框架的原生请求对象和响应对象
  • 重定向

文档中还有一些其他用法的介绍,比如 @Session() 装饰器,异常处理等,大家可以去阅读文档,或者等以后用到的时候再去查阅。

感谢阅读!

目录
相关文章
|
3月前
|
JavaScript 前端开发
08_nest.js控制器详解
08_nest.js控制器详解
43 4
|
3月前
|
前端开发 NoSQL JavaScript
基于 React + Nest 全栈开发的后台系统
这篇文章介绍了一个基于React+Nest全栈开发的后台系统XmwAdmin。项目包括前端和后端技术栈,线上预览地址和登录信息。作者推荐使用pnpm包管理工具和特定的环境依赖。文章提供了项目的运行和编译代码,以及各个功能模块的介绍。还包括演示图和项目活动以及总结部分。数据库下载链接也提供了,该项目已完成后台的核心功能。
基于 React + Nest 全栈开发的后台系统
|
3月前
|
前端开发
10_nest.js 提供者
10_nest.js 提供者
43 3
|
12月前
|
前端开发 JavaScript Java
【Nest系列】4.Nest 核心概念:模块
本文介绍了经典的 MVC 分层架构,以及各层和 Nest 应用的对照。然后讲解了 Nest 模块的具体定义,使用 @Module 装饰器声明的类,就是模块。@Module() 接收一些信息来描述此模块的构成,主要是声明此模块所依赖的其他模块,控制器,提供者,和要导出的供其他模块所使用的部分。
124 0
【Nest系列】4.Nest 核心概念:模块
|
11月前
|
前端开发 JavaScript 开发者
组件(Component):构建现代应用的秘密武器
组件是现代应用程序开发的关键部分,无论是Web应用、移动应用还是桌面应用,都离不开组件化的思维和实践。在本博客中,我们将深入探讨组件的概念、重要性以及如何有效地利用组件来构建可维护、可扩展的应用程序。
88 0
|
12月前
|
API 网络架构
初识nest.js的controller(入门)
初识nest.js的controller(入门)
|
中间件
基于Gin封装Web框架 - 6. 中间件控制器
基于Gin封装Web框架 - 6. 中间件控制器
288 1
基于Gin封装Web框架 - 6. 中间件控制器
快速入门nest.js(1/10)--Controller
这里的coffee字符将我们的应用程序的/coffee url绑定到了这个控制器上,此时如果直接像 http://localhost:3000/coffees 请求会404,因为我们还没有在这个控制器中设置一个GET路由
87 0
|
中间件
基于Gin封装Web框架 - 7. 控制器优化 - 更好用的控制器模式
基于Gin封装Web框架 - 7. 控制器优化 - 更好用的控制器模式
454 0
基于Gin封装Web框架 - 7. 控制器优化 - 更好用的控制器模式
|
前端开发 JavaScript 容器
【React高级技术】合成事件以及 Test Utilities测试框架在Facebook内部进行测试
【React高级技术】合成事件以及 Test Utilities测试框架在Facebook内部进行测试