我的数据库使用 knex.js,并且我有一个依赖于上一个查询的查询。
示例:
用户表
|用户名(pk) |名字 |姓氏 |
登录表
|用户名(pk/fk) |哈希 |
流程是:
插入用户 > 插入登录
登录依赖于用户,因此如果尚未完成对用户的插入,则会返回错误。
这是我的代码:
const handleSignup = (req, res, db, logger, bcrypt) => {
const {
username,
password,
firstName,
lastName,
} = req.body;
const hash = bcrypt.hashSync(password);
if (username || !firstName || !lastName ) {
res.json({
haveEmpty: true
});
return;
} else {
db.transaction((trx) => {
db.select('*').from('user').where('username', '=', username)
.then(data => {
if (!data[0]) {
db('user')
.returning('*')
.insert({
username: username,
first_name: firstName,
last_name: lastName,
})
.then(user => {
db('login')
.returning('*')
.insert({
username: username,
hash: hash
})
.then(login => {
if (login[0]) {
res.json({
isSuccess: true
});
return;
} else {
res.json({
isSuccess: false
});
return;
}
})
.then(trx.commit)
.catch(err => {
logger.error(err);
trx.rollback;
res.render('pages/error-500');
});
})
.then(trx.commit)
.catch(err => {
logger.error(err);
trx.rollback;
res.render('pages/error-500');
});
} else {
res.json('User already Exist!');
return;
}
})
.then(trx.commit)
.catch(err => {
logger.error(err);
trx.rollback;
res.render('pages/error-500');
});
})
.catch(err => logger.error(err));
}
}
而且我不知道我是否正确使用了交易。但这就是我想到的。之前,当我将查询分成两个 promise 时,我收到错误,因为似乎第一个插入(用户)未完成。
此代码可以正常工作,但我知道有更正确的编码方法。
最佳答案
根据我的经验,一旦你不再试图将所有 promise 都塞进同一个函数中, promise 就会变得更加自然! (但是我们所有人都可能曾经写过与您的示例类似的内容,请不要担心。)
较小的代码块往往也更容易测试和调试。例如,如果您知道对请求正文中的变量的检查是正确的,那么问题可能出在堆栈的更深处。
这是一个使用小型中间件堆栈的示例。这允许将操作分解为小块,同时仍然保证一件事会在另一件事之前发生。
const bcrypt = require("bcrypt");
const express = require("express");
const knex = require("knex");
const config = require("./knexfile").development;
const app = express();
app.use(express.json());
const db = knex(config);
const detailValidator = (req, res, next) => {
// You can do more robust validation here, of course
if (!req.body.firstName || !req.body.lastName) {
return next(new Error("Missing user details."));
}
next();
};
const userUniqueValidator = (req, res, next) => {
db("users")
.where("username", req.body.username)
.then(users => {
if (users.length !== 0) {
return next(new Error("User exists."));
}
next();
});
};
const userCreator = (req, res, next) => {
const { username, password, firstName, lastName } = req.body;
const hash = bcrypt.hashSync(password, 10);
db.transaction(trx =>
trx("users")
.insert({
username,
first_name: firstName,
last_name: lastName
})
.then(([userId]) => trx("auth").insert({ user_id: userId, hash }))
.then(() => res.json({ success: true }))
).catch(err => next(err));
};
app.post("/", detailValidator, userUniqueValidator, userCreator);
app.use((err, req, res, next) => res.json({ error: err.message }));
app.listen(4000, () => console.log("yup"));
关于 Knex 中的事务:如果使用上述语法,您实际上根本不需要调用 commit
。但是,您确实需要使用 trx 参数作为查询构建器。该文档还建议了另一个选项,即 transacting
语法:请参阅 docs .
最后,我真的不建议使用您的用户名作为主键。它们经常需要更改,而且总是存在意外泄漏 URL 或日志的风险。不过,我建议包括一个独特的约束。也许是这样的?
exports.up = knex =>
knex.schema.createTable("users", t => {
t.increments("id");
t.string("username").unique();
t.string("first_name");
t.string("last_name");
});
exports.up = knex =>
knex.schema.createTable("auth", t => {
t.increments("id");
t.integer("user_id").references("users.id");
t.string("hash");
});
值得注意的是,我在这个快速示例中使用了 SQLite3,它仅支持在插入后返回行 id(因此在用户插入后 then
子句中使用 [ userId ]
)。
关于node.js - 如何优化 Promise 以避免回调/Promise hell ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55912652/