在使用 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
的字段隐藏需求,全局拦截器是最佳选择。
- 如果有大量涉及
综合使用这些方法可以确保项目的安全性和代码的可维护性。