在nest.js 中,是不是可以在参数装饰器中获取服务实例?
Posted
技术标签:
【中文标题】在nest.js 中,是不是可以在参数装饰器中获取服务实例?【英文标题】:In nest.js, is it possible to get service instance inside a param decorator?在nest.js 中,是否可以在参数装饰器中获取服务实例? 【发布时间】:2019-08-28 20:39:44 【问题描述】:我想使用nest.js 实现这样的目标: (与 Spring 框架非常相似)
@Controller('/test')
class TestController
@Get()
get(@Principal() principal: Principal)
阅读文档数小时后,我发现nest.js 支持创建自定义装饰器。所以我决定实现我自己的@Principal
装饰器。装饰器负责从 http 头中检索访问令牌,并使用令牌从我自己的身份验证服务中获取用户的主体。
import createParamDecorator from '@nestjs/common';
export const Principal = createParamDecorator((data: string, req) =>
const bearerToken = req.header.Authorization;
// parse.. and call my authService..
// how to call my authService here?
return null;
);
但问题是我不知道如何在装饰器处理程序中获取我的服务实例。可能吗?如何?提前谢谢你
【问题讨论】:
【参考方案1】:适用于 NestJS v7
创建自定义管道
// parse-token.pipe.ts
import ArgumentMetadata, Injectable, PipeTransform from '@nestjs/common';
import AuthService from './auth.service';
@Injectable()
export class ParseTokenPipe implements PipeTransform
// inject any dependency
constructor(private authService: AuthService)
async transform(value: any, metadata: ArgumentMetadata)
console.log('additional options', metadata.data);
return this.authService.parse(value);
将此管道与属性装饰器一起使用
// decorators.ts
import createParamDecorator, ExecutionContext from '@nestjs/common';
import ParseTokenPipe from './parse-token.pipe';
export const GetToken = createParamDecorator((data: unknown, ctx: ExecutionContext) =>
return ctx.switchToHttp().getRequest().header.Authorization;
);
export const Principal = (additionalOptions?: any) => GetToken(additionalOptions, ParseTokenPipe);
在有或没有其他选项的情况下使用这个装饰器
@Controller('/test')
class TestController
@Get()
get(@Principal(hello: "world") principal)
【讨论】:
【参考方案2】:无法将服务注入自定义装饰器。
相反,您可以创建一个AuthGuard
来访问您的服务。然后守卫可以向 request
对象添加一个属性,然后您可以使用自定义装饰器访问该属性:
@Injectable()
export class AuthGuard implements CanActivate
constructor(private authService: AuthService)
async canActivate(context: ExecutionContext): Promise<boolean>
const request = context.switchToHttp().getRequest();
const bearerToken = request.header.Authorization;
const user = await this.authService.authenticate(bearerToken);
request.principal = user;
// If you want to allow the request even if auth fails, always return true
return !!user;
import createParamDecorator from '@nestjs/common';
export const Principal = createParamDecorator((data: string, req) =>
return req.principal;
);
然后在你的控制器中:
@Get()
@UseGuards(AuthGuard)
get(@Principal() principal: Principal)
// ...
请注意,nest 提供了一些用于身份验证的标准模块,请参阅docs。
【讨论】:
【参考方案3】:您可以对所有控制器使用中间件。
auth.middleware.ts
interface AccountData
accId: string;
iat: number;
exp: number;
interface RequestWithAccountId extends Request
accId: string;
@Injectable()
export class AuthMiddleware implements NestMiddleware
constructor(private readonly authenticationService: AuthenticationService)
async use(req: RequestWithAccountId, res: Response, next: NextFunction)
const token =
req.body.token || req.query.token || req.headers['authorization'];
if (!token)
throw new UnauthorizedException();
try
const
accId,
: AccountData = await this.authenticationService.verifyToken(token);
req.accId = accId;
next();
catch (err)
throw new UnauthorizedException();
然后创建 AccountId 装饰器
account-id.decorator.ts
import
createParamDecorator,
ExecutionContext,
UnauthorizedException,
from '@nestjs/common';
export const AccountId = createParamDecorator(
(data: unknown, ctx: ExecutionContext) =>
const req = ctx.switchToHttp().getRequest();
const token = req.accId;
if (!token)
throw new UnauthorizedException();
return token;
,
);
然后在你的控制器中应用 AccountId 装饰器
your.controller.ts
@Get()
async someEndpoint(
@AccountId() accountId,
)
console.log('accountId',accontId)
【讨论】:
使用async someEndPoint (@Request() req) return req.accId
更简单,不需要使用装饰器...以上是关于在nest.js 中,是不是可以在参数装饰器中获取服务实例?的主要内容,如果未能解决你的问题,请参考以下文章
如何在没有守卫装饰器的情况下始终验证 JWT? (Nest.js + 护照)