Express/TypeScript解析JSON请求体时如何白名单过滤User类属性
解决方案
你不需要手动逐字段赋值,Express + TypeScript 生态有成熟的标准化方案解决请求体字段白名单过滤问题,核心是在运行时做字段校验和裁剪——注意TS的类型注解仅在编译阶段生效,let user: User = request.body这种写法没有任何运行时防护,是安全隐患的根源。
常用的落地方式有三种,按项目场景选择即可:
方案1:类校验+类转换(TS面向对象风格项目首选)
这是TS后端领域通用性最高的标准方案,用class-validator做字段规则校验,class-transformer做普通JSON对象到类实例的转换,转换过程会自动过滤未在白名单内、或标记为排除的字段。
使用步骤:
- 改造User类,给允许用户提交的字段加校验规则,给敏感禁止提交的字段加排除标记:
import { IsString, IsNumber, Exclude } from 'class-validator'; import { Expose } from 'class-transformer'; export class User { @IsString() @Expose() public Username: string; @IsString() @Expose() public Password: string; @IsString() @Expose() public Email: string; // 敏感字段标记为排除,转换时自动丢弃用户传入的值 @Exclude() public EmailVerified: boolean; @IsString() @Expose() public Country: string; @IsString() @Expose() public State: string; @IsString() @Expose() public City: string; @IsString() @Expose() public Gender: string; @IsNumber() @Expose() public BirthdayTicks: number; }
- 在接口逻辑里做转换和校验,多余字段、敏感字段会被自动过滤,类型不符合的请求直接返回400错误:
import { plainToInstance } from 'class-transformer'; import { validate } from 'class-validator'; app.post('/users', express.json(), async (request, response, next) => { try { console.log(`${request.method} ${request.url} was called.`); // 把请求体转换为真正的User类实例,自动过滤非白名单字段 const user = plainToInstance(User, request.body, { excludeExtraneousValues: true // 关键配置:自动剔除类里未标记@Expose的字段 }); // 校验字段格式 const validationErrors = await validate(user); if (validationErrors.length > 0) { return response.status(400).send(validationErrors); } const sessionId: string = request.query.sessionId as string; const captcha: string = request.query.captcha as string; const createErrors: string[] = await UserStore.CreateUser(elastic, smtp, user, sessionId, captcha); response.status(createErrors.length <= 0 ? 201 : 400).send(createErrors); } catch (error) { next(error); } });
方案2:白名单字段拾取(轻量小项目首选)
如果不想引入额外的装饰器依赖,直接定义允许用户传入的字段列表,从请求体里把对应字段摘出来即可,逻辑完全可控,几行代码就能实现:
// 定义允许用户提交的字段白名单 const ALLOWED_USER_FIELDS = [ 'Username', 'Password', 'Email', 'Country', 'State', 'City', 'Gender', 'BirthdayTicks' ] as const; app.post('/users', express.json(), async (request, response, next) => { try { console.log(`${request.method} ${request.url} was called.`); // 仅从请求体中拾取白名单内的字段,其余字段全部丢弃 const user = ALLOWED_USER_FIELDS.reduce((res, field) => { if (request.body[field] !== undefined) { res[field] = request.body[field]; } return res; }, {} as Pick<User, typeof ALLOWED_USER_FIELDS[number]>); // 可补充基础类型校验逻辑,比如判断BirthdayTicks是否为数字 const sessionId: string = request.query.sessionId as string; const captcha: string = request.query.captcha as string; const errors: string[] = await UserStore.CreateUser(elastic, smtp, user, sessionId, captcha); response.status(errors.length <= 0 ? 201 : 400).send(errors); } catch (error) { next(error); } });
如果项目里已经引入了通用工具库,直接用内置的对象拾取方法就能实现同样的效果,不用自己写遍历逻辑。
方案3:Schema式校验(函数式风格项目首选)
如果不想把校验逻辑和类绑定,可以用Zod这类Schema校验库,直接定义允许的字段结构和类型,解析时自动过滤多余字段,同时自动生成TS类型,不用重复写类型定义:
import { z } from 'zod'; // 定义用户提交的字段Schema,未声明的字段会被自动过滤 const UserCreateSchema = z.object({ Username: z.string(), Password: z.string(), Email: z.string().email(), Country: z.string(), State: z.string(), City: z.string(), Gender: z.string(), BirthdayTicks: z.number() }); app.post('/users', express.json(), async (request, response, next) => { try { console.log(`${request.method} ${request.url} was called.`); // 解析校验请求体,多余字段自动剔除,类型错误直接返回失败 const parseResult = UserCreateSchema.safeParse(request.body); if (!parseResult.success) { return response.status(400).send(parseResult.error.issues); } const user = parseResult.data; const sessionId: string = request.query.sessionId as string; const captcha: string = request.query.captcha as string; const errors: string[] = await UserStore.CreateUser(elastic, smtp, user, sessionId, captcha); response.status(errors.length <= 0 ? 201 : 400).send(errors); } catch (error) { next(error); } });
注意事项
- 所有方案的核心都是在数据传入数据库/ES之前做运行时的字段裁剪,绝对不要信任客户端传入的任何数据
- 类似
EmailVerified这种代表用户权限、状态的字段,永远不要从客户端请求体里取值,应该在服务端逻辑里默认赋值(比如新注册用户默认EmailVerified = false)
内容的提问来源于stack exchange,提问作者Alexandru
相关产品推荐
相关产品推荐

