You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express+GCloud应用Promise未处理警告及注册哈希异常求助

问题排查与修复方案

核心问题分析

  1. 注册接口:未解析Promise直接存入数据库
    hashPass返回的是Promise对象,你直接把它赋值给userHash并插入Datastore,导致数据库存储的是Promise而非加密后的哈希字符串,这是插入报错的直接原因。

  2. 登录接口:Promise同步判断逻辑错误
    authUser中getUser(username)返回Promise,你直接访问.hash会得到undefined,因为Promise还未解析。同时登录接口里用const auth = authUser(...)后直接判断auth === false,这是同步逻辑,而auth实际是Promise对象,永远不会等于false,导致登录逻辑完全失效(只是看起来正常,实际根本没做验证)。

  3. 未处理的Promise拒绝警告
    部分catch块仅打印错误但未将错误传递出去,导致Promise被静默拒绝,触发unhandled promise rejection警告。

修复后的完整代码

var express = require('express');
var router = express.Router();

const {Datastore} = require('@google-cloud/datastore');
const datastore = new Datastore();

const bcrypt = require('bcrypt');

// 获取用户,返回用户对象(处理Promise解析)
async function getUser(username){
  try {
    const [user] = await datastore.get(datastore.key(['user', username]));
    return user;
  } catch (error) {
    console.error(error);
    throw error; // 抛出错误以便上层处理
  }
}

// 验证用户,返回布尔值
async function authUser(username, password){
  const user = await getUser(username);
  if (!user) return false; // 用户不存在直接返回false
  try {
    return await bcrypt.compare(password, user.hash);
  } catch (error) {
    console.error(error);
    throw error;
  }
}

// 加密密码,返回哈希字符串
async function hashPass(password){
  try {
    return await bcrypt.hash(password, 10);
  } catch (error) {
    console.error(error);
    throw error;
  }
}

// 登录接口:改用async/await处理异步逻辑
router.post('/login', async function(req, res, next) {
  try {
    const isAuthenticated = await authUser(req.body.username, req.body.password);
    if (!isAuthenticated) {
      return res.redirect('/login');
    }
    req.session.loggedIn = true;
    req.session.username = req.body.username;
    req.session.save(function(err) {
      if(err){console.error(err);}
      res.redirect('/users/show/'+req.body.username);
    });
  } catch (error) {
    next(error); // 传递错误给Express错误处理中间件
  }
});

router.get('/logout', function(req, res, next) {
  req.session.destroy(function(err){
    if(err){console.error(err);}
    res.redirect('/');
  });
});

router.get('/signup', function(req, res, next){
  res.redirect('/users/new');
});

// 注册接口:等待哈希生成后再插入数据库
router.post('/signup', async function(req, res, next) {
  try {
    if(req.body.notarobot !== "true"){
      return res.redirect('/');
    }
    if(req.body.password !== req.body.confirm){
      req.session.error = "Passwords don't match";
      console.error("Passwords don't match");
      return res.redirect('/signup');
    }
    // 等待密码哈希生成
    const userHash = await hashPass(req.body.password);
    await datastore.insert({
      key: datastore.key(['user', req.body.username]),
      data: {
        username: req.body.username,
        hash: userHash
      }
    });
    console.log("successful entry");
    req.session.loggedIn = true;
    req.session.username = req.body.username;
    req.session.save(function(err) {
      if(err){console.error(err);}
      res.redirect('/users/show/'+req.body.username);
    });
  } catch (error) {
    console.error(error);
    if (error.code === 6) { // Datastore重复键错误码
      req.session.error = "User already exists, choose a new username";
    } else {
      req.session.error = "Registration failed, please try again";
    }
    res.redirect('/signup');
  }
});

module.exports = router;

关键修复点说明

  • 所有异步函数改用async/await,让逻辑更清晰,避免Promise嵌套。
  • getUser中解构Datastore返回的数组(Datastore.get返回的是[实体, 响应]),之前的代码直接返回user会是包含实体的数组,导致后续取hash错误。
  • 所有catch块在打印错误后抛出错误,确保Promise拒绝被上层处理,消除unhandled promise rejection警告。
  • 注册接口中用await等待hashPass完成,确保插入数据库的是哈希字符串而非Promise。
  • 登录接口中等待authUser的Promise解析,再进行判断,修复了之前同步判断的逻辑错误。
  • 增加了Datastore重复键错误的判断(错误码6),更精准处理用户已存在的情况。

内容的提问来源于stack exchange,提问作者Chris

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 05:22:34