扩展请求接口以添加固定用户 属性 并扩展任何其他 class
Extends the Request interface to add a fixed user property and extend any other class
我正在用 NestJS and TypeScript in combination with the implementation of Passport JWT 做服务器端应用程序。
先了解一下上下文:
我的 JwtStrategy (这里没有问题):
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private userService: UserService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'hi',
});
}
async validate(payload: IJwtClaims): Promise<UserEntity> {
const { sub: id } = payload;
// Find the user's database record by its "id" and return it.
const user = await this.userService.findById(id);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
根据有关 validate()
方法的文档:
Passport will build a user object based on the return value of our
validate() method, and attach it as a property on the Request object.
由于这种行为,我可以像这样在我的处理程序中访问 user
对象:
@Get('hi')
example(@Req() request: Request) {
const userId = (request.user as UserEntity).id;
}
您是否注意到我使用了类型断言(告诉编译器将用户对象视为 UserEntity)?没有它,我将无法自动完成我的实体的属性。
作为一个快速的解决方案,我创建了一个 class 来扩展 Request
接口并包含我自己的 UserEntity
类型的 属性。
import { Request } from 'express';
import { UserEntity } from 'entities/user.entity';
export class WithUserEntityRequestDto extends Request {
user: UserEntity;
}
现在,我的处理程序将是:
@Get('hi')
example(@Req() request: WithUserEntityRequestDto) {
const userId = request.user.id; // Nicer
}
现在的真题:
我有(并且将会有更多)一个将接收有效负载的处理程序,让我们为这个例子调用它 PasswordResetRequestDto
。
export class PasswordResetRequestDto {
currentPassword: string;
newPassword: string;
}
处理程序将是:
@Get('password-reset')
resetPassword(@Body() request: PasswordResetRequestDto) {
}
现在,我无权访问用户的对象。我想访问它以了解发出此请求的用户是谁。
我试过的:
使用 TypeScript 泛型并将新的 属性 添加到我之前的 WithUserEntityRequestDto
class 中,如下所示:
export class WithUserEntityRequestDto<T> extends Request {
user: UserEntity;
newProp: T;
}
处理程序将是:
@Get('password-reset')
resetPassword(@Req() request: WithUserEntityRequestDto<PasswordResetRequestDto>) {
}
但现在 PasswordResetRequestDto
将在 newProp
之下,使其成为不可扩展的解决方案。我作为泛型传递的任何类型都将在 newProp
下。另外,我不能扩展 T
因为一个 class 不能扩展两个 class。我不认为自己一直都这样做 classes。
我希望完成的事情:
将类型传递给我的 WithUserEntityRequestDto
class 以包含传递的类型属性以及默认情况下的用户对象。例如,我可以做的一种方式:
request: WithUserEntityRequestDto<AwesomeRequestDto>
request: WithUserEntityRequestDto<BankRequestDto>
值将类似于:
{
user: UserEntity, // As default, always present
// all the properties of the passed type (T),
// all the properties of the Request interface
}
我的目标是找到一种简单且可扩展的方法来扩展 Request
接口并在其上包含任何 type/class,同时始终存在用户对象 (UserEntity
)。
感谢抽出宝贵时间,任何 help/advice/approach 将不胜感激。
Nestjs 为您的问题提供了一个优雅的解决方案,即Custom decoration
将属性附加到请求对象是常见的做法。然后你在每个路由处理程序中手动提取它们,
你要做的就是创建一个用户装饰器:
//user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
然后你可以像这样简单地在你的控制器中使用它:
@Get('hi')
example(@Req() request: Request,@User() user: UserEntity) {
const userId = user.id;
}
我正在用 NestJS and TypeScript in combination with the implementation of Passport JWT 做服务器端应用程序。
先了解一下上下文:
我的 JwtStrategy (这里没有问题):
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor(private userService: UserService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: 'hi',
});
}
async validate(payload: IJwtClaims): Promise<UserEntity> {
const { sub: id } = payload;
// Find the user's database record by its "id" and return it.
const user = await this.userService.findById(id);
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
根据有关 validate()
方法的文档:
Passport will build a user object based on the return value of our validate() method, and attach it as a property on the Request object.
由于这种行为,我可以像这样在我的处理程序中访问 user
对象:
@Get('hi')
example(@Req() request: Request) {
const userId = (request.user as UserEntity).id;
}
您是否注意到我使用了类型断言(告诉编译器将用户对象视为 UserEntity)?没有它,我将无法自动完成我的实体的属性。
作为一个快速的解决方案,我创建了一个 class 来扩展 Request
接口并包含我自己的 UserEntity
类型的 属性。
import { Request } from 'express';
import { UserEntity } from 'entities/user.entity';
export class WithUserEntityRequestDto extends Request {
user: UserEntity;
}
现在,我的处理程序将是:
@Get('hi')
example(@Req() request: WithUserEntityRequestDto) {
const userId = request.user.id; // Nicer
}
现在的真题:
我有(并且将会有更多)一个将接收有效负载的处理程序,让我们为这个例子调用它 PasswordResetRequestDto
。
export class PasswordResetRequestDto {
currentPassword: string;
newPassword: string;
}
处理程序将是:
@Get('password-reset')
resetPassword(@Body() request: PasswordResetRequestDto) {
}
现在,我无权访问用户的对象。我想访问它以了解发出此请求的用户是谁。
我试过的:
使用 TypeScript 泛型并将新的 属性 添加到我之前的 WithUserEntityRequestDto
class 中,如下所示:
export class WithUserEntityRequestDto<T> extends Request {
user: UserEntity;
newProp: T;
}
处理程序将是:
@Get('password-reset')
resetPassword(@Req() request: WithUserEntityRequestDto<PasswordResetRequestDto>) {
}
但现在 PasswordResetRequestDto
将在 newProp
之下,使其成为不可扩展的解决方案。我作为泛型传递的任何类型都将在 newProp
下。另外,我不能扩展 T
因为一个 class 不能扩展两个 class。我不认为自己一直都这样做 classes。
我希望完成的事情:
将类型传递给我的 WithUserEntityRequestDto
class 以包含传递的类型属性以及默认情况下的用户对象。例如,我可以做的一种方式:
request: WithUserEntityRequestDto<AwesomeRequestDto>
request: WithUserEntityRequestDto<BankRequestDto>
值将类似于:
{
user: UserEntity, // As default, always present
// all the properties of the passed type (T),
// all the properties of the Request interface
}
我的目标是找到一种简单且可扩展的方法来扩展 Request
接口并在其上包含任何 type/class,同时始终存在用户对象 (UserEntity
)。
感谢抽出宝贵时间,任何 help/advice/approach 将不胜感激。
Nestjs 为您的问题提供了一个优雅的解决方案,即Custom decoration
将属性附加到请求对象是常见的做法。然后你在每个路由处理程序中手动提取它们,
你要做的就是创建一个用户装饰器:
//user.decorator.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
export const User = createParamDecorator(
(data: unknown, ctx: ExecutionContext) => {
const request = ctx.switchToHttp().getRequest();
return request.user;
},
);
然后你可以像这样简单地在你的控制器中使用它:
@Get('hi')
example(@Req() request: Request,@User() user: UserEntity) {
const userId = user.id;
}