L.Leverage Learn从第一行代码,到读懂真实后端查看项目源码

NestJS 后端开发 / 第 13 课

NestJS 数据校验与错误响应

用 DTO 和 ValidationPipe 检查请求数据,再用异常过滤器与 HTTP 状态码说明失败原因。

◷ 预计 25 分钟 · 面向初学者

Express 例子里我们用 if 检查 req.body.name。字段多了以后,检查散落在各个路由中容易遗漏。NestJS 通常把请求形状和规则写进 DTO(数据传输对象),再由管道验证传入值。注意,TypeScript 的 name: string 只在编译时约束代码,不会自动检查网络上传来的 JSON。

描述输入并在入口校验

下面是按公开字段规则整理的教学 DTO,省略 API 文档标记和可选字段。字段后的 ! 告诉 TypeScript 它会在创建后由框架填入;它不会执行运行时校验。

import { IsInt, IsNumber, IsString, MaxLength, Min, MinLength } from 'class-validator';

export class CreateSubmissionDto {
  @IsInt() @Min(1)
  problemId!: number;

  @IsString() @MinLength(1) @MaxLength(65536)
  code!: string;

  @IsNumber()
  language!: number;
}

每个装饰器都为对应属性声明一条规则:题目 ID 是至少为 1 的整数,代码是长度不超过 65536 的字符串,语言字段是数字。公开版本里 language 确实用 @IsNumber(),例如 Python 3 的编号是 9;不要把它误写成字符串 "python"。这些规则验证形状和值的类型,不代表编号一定受评测器支持。Service 仍需检查有效题目、支持的语言以及当前用户是否有权提交。

在启动时注册全局管道,路由接收 DTO 后就会先校验,再调用控制器方法:

app.useGlobalPipes(new ValidationPipe({
  whitelist: true,
  forbidNonWhitelisted: true,
}));

whitelist 会去掉 DTO 未声明的属性;同时使用 forbidNonWhitelisted 时,多余字段会被拒绝,便于客户端尽早发现拼错的键。若需要把 JSON 字符串转换成数字等类型,要明确配置转换,并检查转换后的值;不要假设声明的 TypeScript 类型会自动改变输入。

错误要有稳定的 HTTP 含义

校验失败通常是 400 Bad Request:客户端可以改正输入后重试。401 Unauthorized 表示没有有效身份凭证;403 Forbidden 表示已识别身份但无权执行;404 Not Found 表示资源不存在或不应向调用方透露;500 Internal Server Error 表示服务器未预期的故障。成功读取常用 200,创建资源常用 201。不要把所有失败都返回 200,也不要把内部堆栈和数据库细节放进响应。

抛出 Nest 的异常类后,内置异常层会生成 HTTP 响应。异常过滤器可统一增加时间、路径等字段。项目的过滤器会保留异常状态和消息结构,并记录 5xx 的错误;日志不要包含密码、JWT 或提交代码。若校验管道没有注册,DTO 规则不会因类型声明而自动运行,错误输入可能继续进入控制器。因此要在应用启动时核实全局管道确实生效,并用无效输入的接口测试检查状态码和响应体。客户端可以据此区分可修正的字段错误与服务器故障。比如传入 { "problemId": 0, "code": "", "language": "python" } 会在到达创建逻辑前校验失败。若 { "problemId": 4, "code": "print(1)", "language": 999 } 通过 DTO,则 Service 仍可能因语言未受支持而返回 400。把格式校验与业务校验分开,才知道该改输入规则还是领域条件。

小练习

收到 {"problemId":2,"code":"x","language":9,"admin":true},并启用了上面两项管道设置,额外的 admin 会怎样?如果合法 DTO 指向不存在的题目,应由哪一层处理?

**答案:**因为启用了 forbidNonWhitelisted,多余字段会导致请求被拒绝,而不是被静默接受。题目是否存在是业务规则,应由 Service 查询并处理,不是 DTO 能判断的。

固定版本源码

互动练习:验证 DTO 并拒绝脏输入

创建用户的 DTO 要求 email 是有效邮箱、age 是整数且至少 18;请求还带了未定义字段。希望校验 JSON 字段并拒绝额外字段,应如何配置?

选择一个答案

✓
这一课,你已经能……

DTO 描述输入规则,ValidationPipe 在业务处理前执行校验,异常过滤器把失败转换成一致的 HTTP 响应。