在使用 TypeORM 的项目中,如果不希望查询返回 password
字段,可以通过以下几种方式实现:
1. 使用 @Exclude
装饰器(推荐)
通过 class-transformer
的 @Exclude
装饰器隐藏字段,使得返回的对象在序列化时不会包含 password
。
实现步骤:
-
安装
class-transformer
:npm install class-transformer
-
在
User
实体中添加@Exclude
:import { Exclude } from 'class-transformer'; import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';@Entity('users') export class User {@PrimaryGeneratedColumn('uuid')id: string;@Column()username: string;@Exclude() // 使用 Exclude 隐藏 password 字段@Column()password: string;@Column()email: string; }
-
在返回时应用
class-transformer
的plainToClass
:
在控制器或服务中将查询结果转化为实例对象,并自动排除标记为@Exclude
的字段。import { plainToInstance } from 'class-transformer'; import { User } from './entities/user.entity';const users = await this.userRepository.find(); return plainToInstance(User, users); // 将 User 实体转化为响应对象
2. 使用 select
指定查询字段
如果某些查询仅需要部分字段,可以在 TypeORM 查询时通过 select
显式排除 password
:
示例:
const users = await this.userRepository.find({select: ['id', 'username', 'email'], // 仅查询需要的字段
});
3. 使用 @Column({ select: false })
TypeORM 提供了 select: false
属性,使得 password
默认不被查询到,但在需要时仍可以显式加载。
修改实体:
@Entity('users')
export class User {@PrimaryGeneratedColumn('uuid')id: string;@Column()username: string;@Column({ select: false }) // 默认不查询 password 字段password: string;@Column()email: string;
}
查询时显式加载 password
:
const user = await this.userRepository.findOne({where: { id: userId },select: ['id', 'username', 'password'], // 显式加载 password
});
4. 使用 DTO 转换响应对象
通过使用 DTO(数据传输对象)来控制返回的字段,从根本上避免 password
被直接返回。
定义 DTO:
export class UserResponseDto {id: string;username: string;email: string;
}
在服务中转换为 DTO:
import { UserResponseDto } from './dto/user-response.dto';const users = await this.userRepository.find();
return users.map(user => new UserResponseDto(user));
5. 结合全局拦截器
如果所有接口返回都需要处理,可以创建一个全局拦截器,自动过滤掉敏感字段。
创建拦截器:
import {Injectable,NestInterceptor,ExecutionContext,CallHandler,
} from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';@Injectable()
export class ExcludePasswordInterceptor implements NestInterceptor {intercept(context: ExecutionContext, next: CallHandler): Observable<any> {return next.handle().pipe(map(data => {if (Array.isArray(data)) {return data.map(user => {delete user.password;return user;});} else if (data && typeof data === 'object') {delete data.password;return data;}return data;}),);}
}
在模块中注册拦截器:
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
import { ExcludePasswordInterceptor } from './interceptors/exclude-password.interceptor';@Module({providers: [{provide: APP_INTERCEPTOR,useClass: ExcludePasswordInterceptor,},],
})
export class AppModule {}
选用建议
-
@Exclude
+class-transformer
:- 最灵活且易维护,适合大型项目。
-
@Column({ select: false })
:- 默认查询不包括
password
,简单有效。
- 默认查询不包括
-
结合全局拦截器:
- 如果有大量涉及
password
的字段隐藏需求,全局拦截器是最佳选择。
- 如果有大量涉及
综合使用这些方法可以确保项目的安全性和代码的可维护性。