【问题标题】:Handle promise rejection in NodeJs在 NodeJs 中处理 Promise 拒绝
【发布时间】:2026-02-02 21:35:01
【问题描述】:

我正在使用创建后端NestJs 库。在我的代码中,我使用了guard 来检查我的令牌是否仍然有效:

import {Injectable, CanActivate, ExecutionContext, HttpException, HttpStatus} from '@nestjs/common';
import { Observable } from 'rxjs';
import * as jwt from 'jsonwebtoken';

@Injectable()
export class AuthGuard implements CanActivate {
    canActivate(
        context: ExecutionContext,
    ): any | Promise<boolean> | Observable<boolean> {
        const request = context.switchToHttp().getRequest();
        const token =  request.headers.authorization.split(' ')[1];
            try {
                const decoded = jwt.verify(token, '123');
                console.log(decoded)
                return true
            } catch(e) {
                console.log('tkn error', e)
                throw new HttpException('User unauthorized', HttpStatus.UNAUTHORIZED);
            }
    }
}

我也有这个service 来检查刷新令牌:

import {HttpException, HttpStatus, Injectable} from '@nestjs/common';
import * as jwt from 'jsonwebtoken';
import {InjectRepository} from "@nestjs/typeorm";
import {User} from "../entities/user.entity";
import {Repository} from "typeorm";

@Injectable()
export class RefreshService {
    constructor(
        @InjectRepository(User)
        private usersRepository: Repository<User>,
    ) {

    }

    async refresh(res, req) {
        const userId =  req.headers['userid'];
        const refreshToken = req.headers.authorization.split(' ')[1];
        const user = await this.usersRepository.findOne({
            where: {
                id: userId,
            },
        });

        if (!refreshToken) {
            throw new HttpException('User unauthorized', HttpStatus.UNAUTHORIZED);
        }
        jwt.verify(refreshToken, 'refresh', function (err, decoded) {
            if (err) {
                console.log(err)
                throw new HttpException('User unauthorized rt', HttpStatus.UNAUTHORIZED);
            } else {
                const token = jwt.sign({foo: 'bar'}, '123', {expiresIn: '55s'});

                res.send({
                    message: "You are logged in",
                    timestamp: new Date().toISOString(),
                    token: token,
                    user: user
                });
            }
        });

        console.log('refresh', refreshToken)
        res.send(refreshToken)
    }
}

即使我使用了try catch,我也会在控制台中收到错误:

[0] (node:1444) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
[0] (node:1444) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

我也明白了:

[0] (node:15492) UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

问题:我在上面的代码中有哪些错误? 如何解决?

【问题讨论】:

  • 你抛出 HttpExeptions 你永远不会捕捉到(至少不是在你展示给我们的代码中)。在第一个片段中,您在 catch 块内抛出异常。该异常将在哪里捕获?而且在第二个 sn-p 中,两个异常(一个在 verify 之前,另一个在 verify 的回调中)都没有在任何地方捕获。
  • 此外,一旦您在第二个 sn-p 中到达 verify 并且该 verify 的回调没有抛出,您将发送两次响应,一次使用 res.send(refreshToken),一次使用res.send({ message: "You are logged in", ...
  • 请仅将代码 sn-ps 用于真正可执行并显示错误的代码
  • @derpirscher,你能告诉我如何更改代码吗?这将非常有帮助

标签: javascript node.js nestjs


【解决方案1】:

你抛出了一些在任何地方都没有发现的异常

canActivate(context: ExecutionContext): any | Promise<boolean> | Observable<boolean> {
    const request = context.switchToHttp().getRequest();
    const token =  request.headers.authorization.split(' ')[1];
    try {
        const decoded = jwt.verify(token, '123');
        console.log(decoded)
        return true
    } catch(e) {
        console.log('tkn error', e)
        //remove the exception here and just return false
        return false;
    }
}

此外,在您的第二个 sn-p 中,您为同一个 res 对象调用了两次 res.send(..),这导致您看到的是 Error [ERR_HTTP_HEADERS_SENT]。此外,当找不到用户时,您可能还需要一些错误处理......

async refresh(res, req) {
    const userId =  req.headers['userid'];
    const refreshToken = req.headers.authorization.split(' ')[1];
    const user = await this.usersRepository.findOne({
        where: {
            id: userId,
        },
    });

    if (user && refreshToken) {
        jwt.verify(refreshToken, 'refresh', function (err, decoded) {
            if (err) {
                console.log(err);
                //remove the exception and send an appropriate response
                res.sendStatus(401);
            } else {
                const token = jwt.sign({foo: 'bar'}, '123', {expiresIn: '55s'});
                res.send({
                    message: "You are logged in",
                    timestamp: new Date().toISOString(),
                    token: token,
                    user: user
                });
            }
        });
    } else {
      //if no user or no refreshToken is found 
      res.sendStatus(401); //send unauthorized status
    }
}

并且只是对过程安全性的提示:您可能应该检查某个地方,如果refreshTokenuser 属于一起。否则,拥有有效refreshToken 的用户可以冒充任何其他用户登录...

【讨论】:

  • res.sendStatus(401);throw new HttpException('User unauthorized', HttpStatus.UNAUTHORIZED); 有什么区别?为什么我写res.sendStatus(401);时没有出现错误?
  • 第一个将结果发送回请求客户端。第二个抛出一个永远不会被捕获的异常(并且可能不会将结果发送回客户端)