序列化:如何排除 json 响应中的实体列而不是 Nestjs 中的内部查询
Serialization: How to exclude Entity columns in json response but not internal queries in Nestjs
编辑:
我看过这个question/answer
但是,如下所示 - 这是从所有查询中排除该字段(在尝试处理用户验证时,使用 findOne 存储库查询排除密码字段 route/controller 没有 ClassSerializerInterceptor
我在 nest.js / typeorm 中有一个实体;我试图从返回的 json 中排除密码字段,但不从我的服务中的任何存储库查询中排除密码字段。例如:
user.entity.ts
:
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn,
UpdateDateColumn, ManyToOne } from 'typeorm';
import { Exclude } from 'class-transformer';
import { Account } from '../accounts/account.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({
unique: true,
})
email: string;
@Column()
password: string;
}
auth.controller.ts
:
import { Controller, Post, Body, Request, Req, Get, UseInterceptors, ClassSerializerInterceptor, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { IUserRequest } from '../../interfaces/user-request.interface';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('/login')
async login(@Request() req: Request) {
const user = await this.authService.checkCredentials(req.body);
return this.authService.logUserIn(user.id);
}
@Get('/profile')
@UseGuards(AuthGuard())
@UseInterceptors(ClassSerializerInterceptor)
async profile(@Request() req: IUserRequest) {
const profile = await this.authService.getLoggedInProfile(req.user.id);
return { profile };
}
}
如果我像这样在密码中添加 Exclude()
@Exclude()
@Column()
password: string;
密码包含在响应中
如果我从密码中删除 Column()
,
@Exclude()
password: string;
密码被排除在响应和所有内部查询之外,例如:
const user = await this.userRepository.findOne({ where: { id }, relations: ['account']});
在 nest.js 中使用 ClassSerializerInterceptor
这可能吗?
如果是这样,请指点正确方向。
您可以跳过属性 depending on the operation。在您的情况下,您将使用:
@Column()
@Exclude({ toPlainOnly: true })
password: string;
这意味着,仅当 class 转换为 json(当您发送响应时)而不是 json 转换为 [=29 时才会跳过该密码=](当你收到请求时)。
然后将 @UseInterceptors(ClassSerializerInterceptor)
添加到您的控制器或控制器方法中。当您 return 时,这会自动将实体 class 转换为 json。
要使 ClassSerializerInterceptor
正常工作,请确保您的实体首先已转换为 class。这可以通过使用带有 { transform: true}
选项的 ValidationPipe
或通过 returning 存储库(数据库)中的实体来自动完成。此外,您必须 return 实体本身:
@Post()
@UseInterceptors(ClassSerializerInterceptor)
addUser(@Body(new ValidationPipe({transform: true})) user: User) {
// Logs user with password
console.log(user);
// Returns user as JSON without password
return user;
}
否则,你必须手动转换它:
async profile(@Request() req: IUserRequest) {
// Profile comes from the database so it will be an entity class instance already
const profile = await this.authService.getLoggedInProfile(req.user.id);
// Since we are not returning the entity directly, we have to transform it manually
return { profile: plainToClass(profile) };
}
建议也看看 TypeOrm hidden-columns
这里您的密码列上有 @Column({select: false})
,所有使用标准查找或查询的请求都将排除密码列。
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({select: false})
password: string;
}
然后在 validations/cases 需要密码的地方
const users = await connection.getRepository(User)
.createQueryBuilder()
.select("user.id", "id")
.addSelect("user.password")
.getMany();
编辑:
我看过这个question/answer
但是,如下所示 - 这是从所有查询中排除该字段(在尝试处理用户验证时,使用 findOne 存储库查询排除密码字段 route/controller 没有 ClassSerializerInterceptor
我在 nest.js / typeorm 中有一个实体;我试图从返回的 json 中排除密码字段,但不从我的服务中的任何存储库查询中排除密码字段。例如:
user.entity.ts
:
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn,
UpdateDateColumn, ManyToOne } from 'typeorm';
import { Exclude } from 'class-transformer';
import { Account } from '../accounts/account.entity';
@Entity()
export class User {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({
unique: true,
})
email: string;
@Column()
password: string;
}
auth.controller.ts
:
import { Controller, Post, Body, Request, Req, Get, UseInterceptors, ClassSerializerInterceptor, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { IUserRequest } from '../../interfaces/user-request.interface';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
@Post('/login')
async login(@Request() req: Request) {
const user = await this.authService.checkCredentials(req.body);
return this.authService.logUserIn(user.id);
}
@Get('/profile')
@UseGuards(AuthGuard())
@UseInterceptors(ClassSerializerInterceptor)
async profile(@Request() req: IUserRequest) {
const profile = await this.authService.getLoggedInProfile(req.user.id);
return { profile };
}
}
如果我像这样在密码中添加 Exclude()
@Exclude()
@Column()
password: string;
密码包含在响应中
如果我从密码中删除 Column()
,
@Exclude()
password: string;
密码被排除在响应和所有内部查询之外,例如:
const user = await this.userRepository.findOne({ where: { id }, relations: ['account']});
在 nest.js 中使用 ClassSerializerInterceptor
这可能吗?
如果是这样,请指点正确方向。
您可以跳过属性 depending on the operation。在您的情况下,您将使用:
@Column()
@Exclude({ toPlainOnly: true })
password: string;
这意味着,仅当 class 转换为 json(当您发送响应时)而不是 json 转换为 [=29 时才会跳过该密码=](当你收到请求时)。
然后将 @UseInterceptors(ClassSerializerInterceptor)
添加到您的控制器或控制器方法中。当您 return 时,这会自动将实体 class 转换为 json。
要使 ClassSerializerInterceptor
正常工作,请确保您的实体首先已转换为 class。这可以通过使用带有 { transform: true}
选项的 ValidationPipe
或通过 returning 存储库(数据库)中的实体来自动完成。此外,您必须 return 实体本身:
@Post()
@UseInterceptors(ClassSerializerInterceptor)
addUser(@Body(new ValidationPipe({transform: true})) user: User) {
// Logs user with password
console.log(user);
// Returns user as JSON without password
return user;
}
否则,你必须手动转换它:
async profile(@Request() req: IUserRequest) {
// Profile comes from the database so it will be an entity class instance already
const profile = await this.authService.getLoggedInProfile(req.user.id);
// Since we are not returning the entity directly, we have to transform it manually
return { profile: plainToClass(profile) };
}
建议也看看 TypeOrm hidden-columns
这里您的密码列上有 @Column({select: false})
,所有使用标准查找或查询的请求都将排除密码列。
import {Entity, PrimaryGeneratedColumn, Column} from "typeorm";
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@Column({select: false})
password: string;
}
然后在 validations/cases 需要密码的地方
const users = await connection.getRepository(User)
.createQueryBuilder()
.select("user.id", "id")
.addSelect("user.password")
.getMany();