Skip to content

控制器

控制器负责处理传入的 HTTP 请求和响应客户端的工作,主要就是处理用户发出的请求和响应。

一个具有 @Controller 装饰器的类,表示这个类是个控制器类。需要在 module 中导入到对应的元数据参数 controllers 列表中,这样 nestIoC容器 才会安装该控制器的实例,才能被你的 Nest 应用所使用。

定义控制器:

ts
// 被 @Controller 装饰的类,是一个控制器类
@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  getHello(): string {
    return this.appService.getHello()
  }
}

注册控制器,添加在模块的 controllers 数组中,这样 Nest 就可以反射出哪些类是需要被容器管理的:

ts
@Module({
	// 注册控制器
  controllers: [AppController]
})
export class AppModule {}

接下来,我们就可以使用控制器处理 HTTP 请求和响应了。

  • 使用不同请求方法 @Get @Post @Put @Delete 等分发处理不同类型的请求
  • 使用 @Body()、@Query()、@Param()、@Request/@Req、@Response/@Res、@Header() 等属性装饰器处理来自 HTTP 请求的参数、请求头
  • 使用 @HttpCode()控制响应状态码、@Redirect()路由重定向 等方法装饰器完成特定需求

请求方法

Nest 中一共有 Get、Post、Delete、Put、Patch、Options 等请求方法,之前提及过符合 RESTFul 常用的请求方法—— Get、Post、Delete、Put

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

  @Get()
  findAll() {}

  @Post()
  create() {}

  @Put()
  update() {}

  @Delete()
  delete() {
    return '删除用户成功'
  }
}

路由通配符

控制器装饰器传递的参数,作为控制器的路径定义,比如 user ,路径支持路由通配符:

  • 动态路由参数:使用冒号 : 可以创建动态路由参数。例如,/users/:id 可以匹配 /users/123,并将 123 作为参数传递给相应组件。
  • 可选动态路由参数:使用括号 () 可以创建可选的动态路由参数。例如,/users/:id(\\d+)? 可以匹配 /users/123/users/。在这个例子中,:id 是可选的,且限定为数字。`
  • 通配符*:用于匹配零个或多个字符(不包括斜杠 /)。例如,/users/* 可以匹配 /users/123/users/admin 等路径。通常用于捕获多级路径。
  • 通配符**:用于匹配任意字符,包括斜杠 /。通常需要配合正则表达式使用。例如,/static/** 可以匹配 /static/js/app.js/static/css/style.css 等路径。通常用于匹配文件资源等。
  • 正则表达式:使用正则表达式可以定义更复杂的匹配规则。例如,/users/\\d+ 可以匹配 /users/123,其中 \\d+ 表示匹配一个或多个数字字符。

参数

数据传输 的章节,已经了解过请求参数大致分为两类:

  • URI路径参数
  • body 请求体参数

之前由 Express 完成的案例,在本节中会由 Nest 完全重制。

TIP

这些方法定义在控制器内,分发处理来自客户端的请求,由于 nest 是由 expressjs和fastjs 封装,所以 expressrequest、responsenest 中也是能够使用的,并且使用方法没有发生太大的改变。

新建一个 User 的模块,完成案例。

query

对于参数的获取,直接使用 Query 装饰器,直接解析获取 query 参数,无需像 Express 通过 req.query 去获取。

Query 装饰器还可以传递参数名,直接解析提取参数。

ts
import { Controller, Get, Query } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('user')
export class UserController {
  @Get()
  findAll(@Query('name') query) {
    return {
      code: 200,
      message: query.name,
    };
  }
  
  // ...
}

Request/Response

Request、Response 装饰器,能让你像在 Express 中使用 req、res 对象一样在 Nest 中使用它们。

在任何请求方法中,都能使用 express.requestObject ,如果你想像在 Express 中操作 requestObject 对象的话。

ts
import { Controller, Get, Query, Req, Res } from '@nestjs/common';
import { UserService } from './user.service';
import { Request, Response } from 'express';

@Controller('user')
export class UserController {
  @Get('req')
  requestObject(@Req() req: Request, @Res() res: Response) {
    res.send({
      code: 200,
      message: req.query.name,
    });
  }
  
  // ...
}

image-20240109001035747

param

TIP

当动态参数和子路由同时存在时,Nest 在处理请求时会按照控制器中路由的定义顺序来匹配路由。当动态路由定义在静态子路由前面时,就会出现冲突。因此,确保动态路由定义在最后方是解决冲突的策略之一。

ts
import { Controller, Get, Param } from '@nestjs/common';

@Controller('user')
export class UserController {
  @Get(':id')
  findOne(@Param('id') id) {
    const mockUsers = [
      {
        id: 1,
        name: '张三',
      },
      {
        id: 2,
        name: '李四',
      },
    ];

    return {
      code: 200,
      data: mockUsers.find((user) => user.id === +id),
    };
  }
  
	// ...
}

image-20240109224256483

body: form-urlencoded

键和值编码为键值元组,以 '&' 分隔,需要指定此次请求的类型为:application/x-www-form-urlencoded

ts
import { Body, Controller,Post } from '@nestjs/common';

@Controller('user')
export class UserController {
  @Post('urlencoded')
  urlencodedCreate(@Body() body) {
    return {
      code: 200,
      data: body,
    };
  }

	// ...
}

image-20240110010714730

body: json

指定请求头的类型为: application/json ,前端的请求库 axios 默认的类型就是 json

ts
import { Body, Controller,Post } from '@nestjs/common';

@Controller('user')
export class UserController {
  @Post('json')
  jsonCreate(@Body() body) {
    return {
      code: 200,
      data: body,
    };
  }
  
  // ...
}

image-20240110002957026

body: form-data

这个类型的参数多数用来传输文件,Nest 解析 form-data 的文件,需要开启一个拦截器

ts
import {
  Body,
  Controller,
  Post,
  UploadedFile,
  UseInterceptors,
} from '@nestjs/common';
import { AnyFilesInterceptor, FileInterceptor } from '@nestjs/platform-express';

@Controller('user')
export class UserController {
  @Post()
  @UseInterceptors(
    AnyFilesInterceptor({
      dest: 'uplodas/',
    }),
  )
  formDataCreate() {}
  
  // ...
}

然后再通过 @UploadedFiles 参数装饰器去取数据,非文件的其他参数,依然使用 @Body 去获取。

ts
import {
  Body,
  Controller,
  Post,
  UploadedFile,
  UseInterceptors,
} from '@nestjs/common';
import { AnyFilesInterceptor, FileInterceptor } from '@nestjs/platform-express';

@Controller('user')
export class UserController {
  @Post('form-data')
  @UseInterceptors(
    AnyFilesInterceptor({
      dest: 'uplodas/',
    }),
  )
  formDataCreate(
    @Body() body,
    @UploadedFiles() files: Array<Express.Multer.File>,
  ) {
    return {
      code: 200,
      data: {
        filename: files[0].filename,
        name: body.name,
      },
    };
  }
}

客户端发送 Post 请求,指定 Content-Typemultipart/form-data

image-20240110011623089

请求头

使用 @Headers 装饰器获取 HTTP 请求的请求头:

ts
import { Controller, Get, Headers } from '@nestjs/common';

@Controller('user')
export class UserController {
 @Get('/get/headers')
 getHeaders(@Headers() headers) {
   return {
     contentType: headers['content-type'],
   };
 }
 
 // ...
}

image-20240110012502428

状态码

返回指定的 HTTP 状态码:

ts
import { Controller, Get, HttpCode } from '@nestjs/common';

@Controller('user')
export class UserController {
  @Get('/get/status-code')
  @HttpCode(204)
  getStatusCode() {}
  
  // ...
}

image-20240110012737657

通常情况下,你的状态码并不是静态的,而是取决于各种因素。在这种情况下,你可以使用特定于库的响应(使用 @Res() 注入)对象(或者在出现错误时抛出异常)。

重定向

重定向到指定的 URL 路径,实际上 Nest 在底层会直接调用 requestObject.redirect()

@Redirect() 接受两个参数,并且 statusCodeurl 都是可选的。如果省略,则默认值 statusCode302

ts
import { Controller, Get, Redirect } from '@nestjs/common';

@Controller('user')
export class UserController {
  @Get('/get/redirect')
  @Redirect('https://nestjs.com', 301)
  redirect() {}
  
  // ...
}

嵌套路由

当程序体量达到一定规模之后,一个控制器可能会包含十几个控制器,控制器映射的路由路径并不是根据模块或引用关系决定的,而是在 @Controller() 中定义的。

比如 user 模块,如果它下面还有 info 子模块,就需要在子模块定义路径:@Controller('user/info')

ts
// user模块控制器
@Controller('user')
export class UserController {}

// user/info 嵌套路径模块控制器
@Controller('user/info')
export class InfoController {
  @Get()
  getNested(@Req() req) {
    return {
      code: 200,
      message: '嵌套路径',
      url: req.url
    }
  }
}

总结

控制器用于处理传入的 HTTP 请求,并根据请求返回响应。主要任务包括:

  • 处理请求:接受请求,解析请求参数,并执行相应的操作。
  • 返回响应:根据请求的结果,构建并返回HTTP 响应。

在处理请求时,其表现与在 Express 中一致,你可以正常返回响应,也可以调用 RequestObject 返回响应。

控制器方法会返回不同类型的值,这些值将会作为 HTTP 响应返回给客户端

  • JSON:返回json对象,Nest 会自动序列化返回给客户端。
  • Promise:Nest 会等待 Promise 完成,并将其结果返回给客户端。
  • Observables:如果你使用 RxJs ,你也可以返回一个 ObservableNest 会订阅 Observable 并发送事件流给客户端。

如有转载或 CV 的请标注本站原文地址