Controller概念
每一个 Controller 都可以依照需求来设计不同 Http Method 的资源,就好像外场服务生负责带位、协助客人点餐一样,并根据客户的需求做出相对应的回应:Controller 负责路由的配置并处理来自客户端的请求,而每一个 Controller 都可以依照需求来设计不同 Http Method 的资源, 就好像外场服务生负责带位、协助客人点餐一样,并根据客户的需求做出相对应的回应:
NestCLI 快速生成 Controller:
NestCLI 快速生成 Controller:
$ nest generate controller <CONTROLLER_NAME>
#**注意**:`<CONTROLLER_NAME>`可以含有路径,如:`features/todo`,这样就会在 `src` 资料夹下建立该路径并含有 Controller。
`src` 底下会看见一个名为 `todo` 的资料夹
nest generate controller todo
Controller发起HTTP请求详解
路由前缀,路由符号(regexp)
-
添加路由前缀的好处是可以使相同路由的资源都归纳在同一个 Controller 里面,其中包含了前缀底下的所有子路由:
-
设计路由时,可能会提供些许的容错空间, 比如说:原本是
GET /todos/examples``/todos/exammmmmmmmples``/todos/exam_ples``/todos/examples``*路由前缀和路由参数演示为一个动图,在下面~~
路由参数@Param,查询参数 @Query
传什么参数就填什么,没什么难的。看个范例
路由前缀和路由参数演示:
查询参数演示:
//路由参数@Param
import { Controller, Get, Param } from '@nestjs/common';
//路由前缀:todos
@Controller('todos')
export class TodoController {
//路由参数:id
@Get(':id')
get(@Param('id') id: string) {
return {
id,
title: `Title ${id}`,
description: ''
};
}
}
//查询参数 @Query
import { Controller, Get, Query } from '@nestjs/common';
@Controller('todos')
export class TodoController {
@Get()
getList(
// 查询参数:limit或skip
@Query('limit') limit: number = 30,
@Query('skip') skip: number = 0
) {
const list = [
{
id: 1,
title: 'Title 1',
description: ''
},
{
id: 2,
title: 'Title 2',
description: ''
}
];
return list.slice(skip, limit);
}
}
主体资料 (@Body),资料传输物件 (Data Transfer Object), 标头 (@Header)
- 在传输资料时经常会使用到主体资料,比如说:
POST、PUT、PATCH等操作,Nest 有提供@Body装饰器来取得主体资料。范例程式码如下:
import { Body, Controller, Post } from '@nestjs/common';
@Controller('todos')
export class TodoController {
@Post()
create(
// 创建body的title,description
@Body('title') title: string,
@Body('description') description?: string
) {
const id = 1;
return { id, title, description };
}
}
- 资料传输物件 (Data Transfer Object)通常用于****过滤、格式化资料,它只负责存放要传递的资讯,故 只有唯读属性,没有任何方法 既然是定义格式,那么就有两种选择:
- TypeScript 的
interface - 标准 JavaScript 支援的
class
基本上会建议大家采用 class 的形式来建立 DTO,原因是 interface 在编译成 JavaScript 就会被删除,而 class 会保留,这对部分功能是有影响的,所以 官方也推荐大家采用 class。
那么就来建立一个范例的 DTO 吧,在要调整的 Controller 目录下,新增一个名为 dto 的资料夹,并建立create-<CONTROLLER_NAME>.dto.ts,我这边的档案名称为create-todo.dto.ts:
export class CreateTodoDto {
public readonly title: string;
public readonly description?: string;
}
// dto格式为只读格式
import { Body, Controller, Post } from '@nestjs/common';
import { CreateTodoDto } from './dto/create-todo.dto';
// 将带有@body装饰器的参数类型指定为该DTO格式
@Controller('todos')
export class TodoController {
@Post()
create(@Body() dto: CreateTodoDto) {
const id = 1;
return { id, ...dto };
}
}
- 有时候可能需要设置标头来回传给用户端,这时候就可以用
@Header装饰器来配置:
import { Controller, Get, Header } from '@nestjs/common';
@Controller('todos')
export class TodoController {
@Get()
// 设置标头回传客户端
@Header('X-Hao-headers', '1')
getAll() {
return {
id: 1,
title: 'Title 1',
description: ''
};
}
}
其他提供许多参数装饰器来提供开发人员取得更多资讯查阅表:
@Request():请求的装饰器,带有此装饰器的参数会赋予底层框架的 请求物件 (Request Object) ,该装饰器有别称,通常将参数名称取为。@Req()``req@Response():回应的装饰器,带有此装饰器的参数会赋予底层框架的 回应物件 (Response Object) ,该装饰器有别称,通常将参数名称取为。@Res()``res@Next():Next 函式的装饰器,带有此装饰器的参数会赋予底层框架的 Next 函式,用途为呼叫下一个 中介软体 (Middleware)@Param(key?: string):路由参数的装饰器,相当于req.params或req.params[key]。@Query(key?: string):查询参数的装饰器,相当于req.query或req.query[key]。@Body(key?: string):主体资料的装饰器,相当于req.body或req.body[key]。@Headers(name?: string):请求标头的装饰器,相当于req.headers或req.headers[name]。@Session():session的装饰器,相当于req.session。@Ip():IP 的装饰器,相当于req.ip。@HostParam():host 的装饰器,相当于req.hosts。
Http Methods & Http Code
添加装饰器在* class *的方法上,来指定不同 Http Method 所呼叫的方法
常见方法查阅表:
@Get:表示接收对应路由且为GET请求时触发。@Post:表示接收对应路由且为POST请求时触发。@Put:表示接收对应路由且为PUT请求时触发。@Patch:表示接收对应路由且为PATCH请求时触发。@Delete:表示接收对应路由且为DELETE请求时触发。@Options:表示接收对应路由且为OPTIONS请求时触发。@Head:表示接收对应路由且为HEAD请求时触发。@All:表示接收对应路由且为以上任何方式的请求时触发。
import { Controller, Patch, HttpCode, HttpStatus } from '@nestjs/common';
@Controller('todos')
export class TodoController {
@Patch()
@HttpCode(HttpStatus.NO_CONTENT)
get() {
return [];
}
}
Controller处理回应的方式
标准模式 return
import { Controller, Get } from '@nestjs/common';
@Controller('todos')
export class TodoController {
@Get()
getAll() {
return [];
}
}
非同步ES7 的async/await
import { Controller, Get } from '@nestjs/common';
@Controller('todos')
export class TodoController {
@Get()
async getAll() {
return new Promise((resolve, reject) => setTimeout(() => resolve([]), 1000));
}
}
RxJS函数库
Nest 会自动订阅 / 取消订阅对象,无须手动取消订阅,
import { Controller, Get } from '@nestjs/common';
import { of } from 'rxjs';
@Controller('todos')
export class TodoController {
@Get()
getAll() {
return of([]);
}
}
底层Express框架的回应物件
import { Controller, Get, Res } from '@nestjs/common';
import { Response } from 'express';
@Controller('todos')
export class TodoController {
@Get()
getAll(@Res() res: Response) {
res.send([]);
}
}
模式的限制
Nest 会去侦测是否有带@Res、@Response、@Next装饰器的参数,如果有的话,该资源就会启用函式库模式,而标准模式会被关闭,这是什么意思呢?简单来说 值的方式会失去作用。
那如果真的要从回应物件中取得资讯,但又想采用标准模式的话,有什么方法可以突破此限制吗?答案是有的,只需要在装饰器中添加 passthrough: true 即可:
import { Controller, Get, Res } from '@nestjs/common';
import { Response } from 'express';
@Controller('todos')
export class TodoController {
@Get()
getAll(@Res({ passthrough: true }) res: Response) {
return [];
}
}