2 回答

TA貢獻1770條經驗 獲得超3個贊
這個功能讓我感到困惑,因為它有很多責任,但我發現了問題:
該connection.query
方法是非阻塞的,這意味著它不會等待它的執行結束以前進到下一條指令。
當您使用異步方法和時Promise
,最好嘗試保持方法的一致性(避免混合回調函數和async
/?await
)。async
如果使用/ ,我已經重構了它應該是什么樣子await
:
app.post('/api/edit-profile', regularFunctions, async function (req, res) {
? ? let email = req.body.email
? ? let password_current = req.body.password_current
? ? let results = await executeQuery(connection, 'SELECT * FROM accounts WHERE id = ?', [req.body.id]);
? ? if (results.length > 0) {
? ? ? ? let isMatch = await comparePassword(password_current, results[0].password);
? ? ? ? if (!isMatch) {
? ? ? ? ? ? throw new Error(`Password doesn't match`);
? ? ? ? }
? ? ? ? let changed = []
? ? ? ? // Password matches
? ? ? ? if (req.body.password_new) {
? ? ? ? ? ? let newPassword = req.body.password_new
? ? ? ? ? ? let hashed_password = await hashPassword(newPassword)
? ? ? ? ? ? let results = await executeQuery(connection, 'UPDATE accounts SET password = ? WHERE id = ?', [hashed_password, req.body.id]);
? ? ? ? ? ? if (results.affectedRows && results.affectedRows > 0) {
? ? ? ? ? ? ? ? changed.push('password')
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? throw new Error('Unable to save settings');
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (req.body.license_key) {
? ? ? ? ? ? let newKey = req.body.license_key
? ? ? ? ? ? let response = await axios.get(`https://voltcloud.net/api/hosting/check-key/${newKey}`, {
? ? ? ? ? ? ? ? headers: {
? ? ? ? ? ? ? ? ? ? authorization: '<redacted>'
? ? ? ? ? ? ? ? }
? ? ? ? ? ? });
? ? ? ? ? ? let data = response.data
? ? ? ? ? ? if (typeof data === 'object') {
? ? ? ? ? ? ? ? if (data.active === 1) {
? ? ? ? ? ? ? ? ? ? let response = await axios({
? ? ? ? ? ? ? ? ? ? ? ? method: 'post',
? ? ? ? ? ? ? ? ? ? ? ? url: `https://voltcloud.net/api/hosting/activate-key/${newKey}`,
? ? ? ? ? ? ? ? ? ? ? ? headers: {
? ? ? ? ? ? ? ? ? ? ? ? ? ? authorization: '<redacted>'
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? })
? ? ? ? ? ? ? ? ? ? if (response.data === 'Success') {
? ? ? ? ? ? ? ? ? ? ? ? let results = await executeQuery(connection, 'UPDATE accounts SET license_key = ? WHERE id = ?', [newKey, req.body.id]);
? ? ? ? ? ? ? ? ? ? ? ? if (results.affectedRows && results.affectedRows > 0) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? changed.push('license key')
? ? ? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? throw new Error('Unable to save settings');
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? } else if (data === 'License already active!') {
? ? ? ? ? ? ? ? ? ? ? ? throw new Error('License key is already active!');
? ? ? ? ? ? ? ? ? ? } else if (data === 'Failed to update key.') {
? ? ? ? ? ? ? ? ? ? ? ? throw new Error('Unable to save settings');
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? throw new Error('Unable to save settings');
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? let results = await executeQuery(connection, 'UPDATE accounts SET email = ? WHERE id = ?', [email,req.body.id]);
? ? ? ? if (results.affectedRows && results.affectedRows > 0) {
? ? ? ? ? ? changed.push('email')
? ? ? ? } else {
? ? ? ? ? ? throw new Error('Unable to save settings');
? ? ? ? }
? ? ? ? let finalTxt = 'Successfully changed, '
? ? ? ? if (changed.length > 1) {
? ? ? ? ? ? changed.forEach(function (txt, index) {
? ? ? ? ? ? ? ? if (index === 0) {
? ? ? ? ? ? ? ? ? ? finalTxt = finalTxt + txt
? ? ? ? ? ? ? ? } else if (index === 2) {
? ? ? ? ? ? ? ? ? ? finalTxt = finalTxt + `and ${txt}.`
? ? ? ? ? ? ? ? }
? ? ? ? ? ? })
? ? ? ? } else if (changed.length === 1) {
? ? ? ? ? ? finalTxt = `Successfully changed ${changed[0]}.`
? ? ? ? }
? ? ? ? res.send(finalTxt)
? ? ? ? res.end();
? ? }
});
function executeQuery(conn, sql, params) {
? ? return new Promise((resolve, reject) => {
? ? ? ? conn.query(sql, params, function (err, data) {
? ? ? ? ? ? if (err != null) {
? ? ? ? ? ? ? ? return reject(err);
? ? ? ? ? ? }
? ? ? ? ? ? return resolve(data);
? ? ? ? });
? ? });
}
function comparePassword(val1, val2) {
? ? return new Promise((resolve, reject) => {
? ? ? ? bcrypt.compare(val1, val2, function (err, isMatch) {
? ? ? ? ? ? if (err != null) {
? ? ? ? ? ? ? ? return reject(err);
? ? ? ? ? ? }
? ? ? ? ? ? resolve(isMatch);
? ? ? ? });
? ? })
}
請注意,我們根本沒有使用回調函數,即使在我們沒有基于本機的Promise函數(即 mysql 連接)的情況下,我們也委托給一個函數來代理回調以提供Promise并保持最終的一致性執行。

TA貢獻1846條經驗 獲得超7個贊
if
原始代碼在發送響應之前并沒有等待兩個分支完成。由于嵌套,很難在回調中構造這樣的代碼。
盡可能使用async
函數await
。它允許更多的可讀代碼和更容易的錯誤處理。所以這個答案更多的是代碼審查,而不是針對您的問題的簡單修復。
拆分出一些在其他路由中有用的通用幫助代碼:
// Generate errors for the web, with context
function responseError(message, status, data){
? ? const error = new Error(message)
? ? error.status = status
? ? for (const key in data){
? ? ? ? error[key] = data[key]
? ? }
? ? return error
}
// Turn mysql callbacks into promises (or use util.promisify)
async function runQuery(query, values){
? ? return new Promise((resolve, reject) => {
? ? ? ? connection.query(query, values, function(error, results){
? ? ? ? ? ? if (error) return reject(error)
? ? ? ? ? ? return resolve(results)
? ? ? ? })
? ? })
}
async function runUpdateQuery(query, values){
? ? const results = await runQuery(query, values)
? ? if (!results) throw responseError('No update result', 500, { query })
? ? if (!results.affectedRows) throw responseError('No affected rows', 400, { query })
? ? return results
}
來自兩個條件的代碼if可以很容易地分開,以及其他帳戶操作。
async function apiAuthUserId(id, password){
? ? const results = await runQuery('SELECT * FROM accounts WHERE id = ?', id)
? ? if (!results.length) throw responseError('No account', 400, { id })
? ? const isMatch = await bcrypt.compare(password_current, results[0].password)
? ? if (!isMatch) throw responseError('Password doesn\'t match', 400)
? ? return true
}
async function apiUpdatePassword(id, password){
? ? let newPassword = req.body.password_new
? ? let hashed_password = await hashPassword(newPassword)
? ? await runUpdateQuery('UPDATE accounts SET password = ? WHERE id = ?', [hashed_password, req.body.id])
? ? return id
}
async function apiUpdateEmail(id, email){
? ? await runUpdateQuery('UPDATE accounts SET email = ? WHERE id = ?', [email, id])
? ? return email
}
async function apiUpdateLicenseKey(id, licenseKey){
? ? const response_license = await axios.get(`https://voltcloud.net/api/hosting/check-key/${licenseKey}`, {
? ? ? ? headers: {
? ? ? ? ? ? authorization: 'somekey'
? ? ? ? }
? ? })
? ? const data = response_license.data
? ? if (!data) {
? ? ? throw responseError('No license key response data', 500, { response: response_license })
? ? }
? ? if (data.active !== 1) {
? ? ? throw responseError('License key not active', 400,? { key: licenseKey })
? ? }
? ? const response_activate = await axios({
? ? ? ? method: 'post',
? ? ? ? url: `https://voltcloud.net/api/hosting/activate-key/${licenseKey}`,
? ? ? ? headers: {
? ? ? ? ? ? authorization: 'somekey'
? ? ? ? }
? ? })
? ? switch (response_activate.data){
? ? ? ? case 'License already active!':
? ? ? ? ? ? throw responseError('License key is already active!', 400, { response: response_activate })
? ? ? ? case 'Failed to update key.':
? ? ? ? ? ? throw responseError('Unable to save settings!', 400, { response: response_activate })
? ? ? ? case 'Success':
? ? ? ? ? ? await runUpdateQuery('UPDATE accounts SET license_key = ? WHERE id = ?', [licenseKey, req.body.id])
? ? ? ? ? ? return licenseKey
? ? ? ? default:
? ? ? ? ? ? throw responseError('Unable to save settings!', 500, { response: response_activate })
? ? }
}
這樣您的路由代碼就可以更簡潔一些,并顯示需要完成的工作,而不是如何完成所有工作。
app.post('/api/edit-profile', regularFunctions, async function (req, res) {
? ? const changed = []
? ? try {
? ? ? ? const { id, email, password_current } = req.body
? ? ? ? await apiAuthUserId(id, password_current)
? ? ? ? // Password matches
? ? ? ? if (req.body.password_new) {
? ? ? ? ? ? await apiUpdatePassword(id, req.body.password_new)
? ? ? ? ? ? changed.push('password')
? ? ? ? }
? ? ? ? // License key
? ? ? ? if (req.body.license_key) {
? ? ? ? ? ? await apiUpdateLicenseKey(id, req.body.license_key)
? ? ? ? ? ? changed.push('license key')
? ? ? ? }
? ? ? ? await apiUpdateEmail(id, email)
? ? ? ? changed.push('email')
? ? ? ? let finalTxt = `Successfully changed ${changed.join(' and ')}.`
? ? ? ? res.send(finalTxt)
? ? }
? ? catch (error) {
? ? ? ? // If your not using transactions, might need to deal with partial `changed` responses here.?
? ? ? ? console.error('Error /api/edit-profile', error)
? ? ? ? res.status(error.status||500).send(`Error: ${error.message}`)
? ? }
});
添加回答
舉報