Bootstrap

在使用 TypeORM 的项目中,如果不希望查询返回 password 字段,可以通过以下几种方式实现

在使用 TypeORM 的项目中,如果不希望查询返回 password 字段,可以通过以下几种方式实现:


1. 使用 @Exclude 装饰器(推荐)

通过 class-transformer@Exclude 装饰器隐藏字段,使得返回的对象在序列化时不会包含 password

实现步骤:
  1. 安装 class-transformer

    npm install class-transformer
    
  2. 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;
    }
    
  3. 在返回时应用 class-transformerplainToClass
    在控制器或服务中将查询结果转化为实例对象,并自动排除标记为 @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 {}

选用建议

  1. @Exclude + class-transformer

    • 最灵活且易维护,适合大型项目。
  2. @Column({ select: false })

    • 默认查询不包括 password,简单有效。
  3. 结合全局拦截器

    • 如果有大量涉及 password 的字段隐藏需求,全局拦截器是最佳选择。

综合使用这些方法可以确保项目的安全性和代码的可维护性。

;