简体   繁体   中英

Custom Route Decorators for NestJS

I've created some custom parameter decorators for my routes, but I do not find any useful documentation on how to create a decorator for the route itself. There is some description how to bundle existing method decorators together which does not help me.

What I'm trying to achieve is some simple scope validation. The scopes are already set up in the request context. What I currently have, only based on TypeScript decorators , but is actually going nowhere:

controller.ts

@RequiredScope(AuthScope.OWNER)
@Get('/some-route')
async get() {
    ...
}

required-scopes.ts

export function RequiredScope(...scopes: string[]) {
    return (target: any, propertyKey: string, descriptor: PropertyDescriptor) => {
        console.log(`Validation of scopes '${scopes.join(',')}' for input '${RequestContext.getScopes().join(',')}'`)
        if (!scopes.map(s => RequestContext.hasOneOfTheScopes(s)).find(valid => !valid)) {
            throw new HttpException(`Missing at least one scope of '${scopes.join(',')}'`, HttpStatus.FORBIDDEN)
        }
    }
}

Problem here is that my request context is not even available because my middleware which does set up my context did not kick in yet. The request is failing immediately.

Can somebody point me into the right direction?

As kindly proposed by Khaled Mohamed (thank you,). solving this with guards was quite simple and a better approach.


Creating a decorator with the required scopes as a param in scopes.decorator.ts :

export const Scopes = (...scopes: string[]) => SetMetadata('scopes', scopes)

Setting up a guard which checks the provided meta scopes and does check if the requirement is fulfilled in scopes.guard.ts :

@Injectable()
export class ScopesGuard implements CanActivate {

    constructor(private reflector: Reflector) {}

    canActivate(
        context: ExecutionContext,
    ): boolean | Promise<boolean> | Observable<boolean> {
        const scopes = this.reflector.get<string[]>('scopes', context.getHandler())
        if (!scopes || scopes.length === 0) return true
        if (!scopes.map(s => RequestContext.hasOneOfTheScopes(s)).find(valid => !valid)) {
            throw new HttpException(`Missing at least one scope of '${scopes.join(',')}'`, HttpStatus.FORBIDDEN)
        }
        return true
    }
}
  

Activating the guard globally in app.module.ts :

@Module({
    imports: [...],
    controllers: [...],
    providers: [
        {
            provide: APP_GUARD,
            useClass: ScopesGuard,
        }
    ],
})

Using the decorator at the route in controller.ts :

@Scopes(AuthScope.OWNER)
@Get('/some-route')
async get() {
    ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM