Middleware chạy trước khi router xác định handler nào sẽ được gọi, nên nó không biết route handler đích là hàm nào và không đọc được metadata gắn trên handler đó. Middleware chỉ có req, res, next kiểu Express thuần.
Guard thì nhận ExecutionContext — có getHandler() và getClass() — nên đọc được metadata qua Reflector:
ts
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const required = this.reflector.getAllAndOverride<string[]>(ROLES_KEY, [
context.getHandler(), // method-level metadata wins
context.getClass(), // falls back to controller-level
])
if (!required) return true
const { user } = context.switchToHttp().getRequest()
return required.some((role) => user.roles?.includes(role))
}
}getAllAndOverride nhận mảng target xếp từ cụ thể tới tổng quát nên metadata ở method ghi đè metadata ở controller.
Middleware vẫn hữu ích cho việc không phụ thuộc route: logging, request-id, gắn cookie parser.