Node.js MySQLerror handling

我已经阅读了几个在node.js中使用mysql的例子,我对error handling有疑问。

大多数例子都是这样的error handling(也许是为了简洁):

app.get('/countries', function(req, res) { pool.createConnection(function(err, connection) { if (err) { throw err; } connection.query(sql, function(err, results) { if (err) { throw err; } connection.release(); // do something with results }); }); }); 

这会导致服务器在每次出现sql错误时崩溃。 我想避免这种情况,并保持服务器运行。

我的代码是这样的:

 app.get('/countries', function(req, res) { pool.createConnection(function(err, connection) { if (err) { console.log(err); res.send({ success: false, message: 'database error', error: err }); return; } connection.on('error', function(err) { console.log(err); res.send({ success: false, message: 'database error', error: err }); return; }); connection.query(sql, function(err, results) { if (err) { console.log(err); res.send({ success: false, message: 'query error', error: err }); return; } connection.release(); // do something with results }); }); }); 

我不确定这是否是最好的处理方法。 我也想知道是否应该有一个connection.release()在查询的err块。 否则,连接可能会保持打开并随着时间的推移而build立。

我习惯了Java的try...catch...finally或者try-with-resources ,我可以“干净地”捕获任何错误,并最终closures所有的资源。 有没有办法把错误传播到一个地方呢?

我已经决定使用es2017语法和Babel来处理它,并将其转储到节点7支持的es2016。

较新版本的Node.js支持这种语法,无需转译。

这里是一个例子:

 'use strict'; const express = require('express'); const router = express.Router(); const Promise = require('bluebird'); const HttpStatus = require('http-status-codes'); const fs = Promise.promisifyAll(require('fs')); const pool = require('./pool'); // my database pool module, using promise-mysql const Errors = require('./errors'); // my collection of custom exceptions //////////////////////////////////////////////////////////////////////////////// // GET /v1/provinces/:id //////////////////////////////////////////////////////////////////////////////// router.get('/provinces/:id', async (req, res) => { try { // get a connection from the pool const connection = await pool.createConnection(); try { // retrieve the list of provinces from the database const sql_p = `SELECT p.id, p.code, p.name, p.country_id FROM provinces p WHERE p.id = ? LIMIT 1`; const provinces = await connection.query(sql_p); if (!provinces.length) throw new Errors.NotFound('province not found'); const province = provinces[0]; // retrieve the associated country from the database const sql_c = `SELECT c.code, c.name FROM countries c WHERE c.id = ? LIMIT 1`; const countries = await connection.query(sql_c, province.country_id); if (!countries.length) throw new Errors.InternalServerError('country not found'); province.country = countries[0]; return res.send({ province }); } finally { pool.releaseConnection(connection); } } catch (err) { if (err instanceof Errors.NotFound) return res.status(HttpStatus.NOT_FOUND).send({ message: err.message }); // 404 console.log(err); return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: err, message: err.message }); // 500 } }); //////////////////////////////////////////////////////////////////////////////// // GET /v1/provinces //////////////////////////////////////////////////////////////////////////////// router.get('/provinces', async (req, res) => { try { // get a connection from the pool const connection = await pool.createConnection(); try { // retrieve the list of provinces from the database const sql_p = `SELECT p.id, p.code, p.name, p.country_id FROM provinces p`; const provinces = await connection.query(sql_p); const sql_c = `SELECT c.code, c.name FROM countries c WHERE c.id = ? LIMIT 1`; const promises = provinces.map(async p => { // retrieve the associated country from the database const countries = await connection.query(sql_c, p.country_id); if (!countries.length) throw new Errors.InternalServerError('country not found'); p.country = countries[0]; }); await Promise.all(promises); return res.send({ total: provinces.length, provinces }); } finally { pool.releaseConnection(connection); } } catch (err) { console.log(err); return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: err, message: err.message }); // 500 } }); //////////////////////////////////////////////////////////////////////////////// // OPTIONS /v1/provinces //////////////////////////////////////////////////////////////////////////////// router.options('/provinces', async (req, res) => { try { const data = await fs.readFileAsync('./options/provinces.json'); res.setHeader('Access-Control-Allow-Methods', 'HEAD,GET,OPTIONS'); res.setHeader('Allow', 'HEAD,GET,OPTIONS'); res.send(JSON.parse(data)); } catch (err) { res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: err, message: err.message }); } }); module.exports = router; 

使用async / awaittry { try { } finally { } } catch { } pattern可以实现干净的error handling,您可以在一个地方收集和处理所有的错误。 无论如何,finally块都会closures数据库连接。

你只需要确保你一直在处理承诺。 对于数据库访问,我使用promise-mysql模块而不是普通的mysql模块。 对于其他一切,我使用bluebird模块和promisifyAll()

我也有自定义的exception类,我可以抛出在某些情况下,然后检测在catch块。 根据哪些exception可以在try块中抛出,我的catch块可能看起来像这样:

 catch (err) { if (err instanceof Errors.BadRequest) return res.status(HttpStatus.BAD_REQUEST).send({ message: err.message }); // 400 if (err instanceof Errors.Forbidden) return res.status(HttpStatus.FORBIDDEN).send({ message: err.message }); // 403 if (err instanceof Errors.NotFound) return res.status(HttpStatus.NOT_FOUND).send({ message: err.message }); // 404 if (err instanceof Errors.UnprocessableEntity) return res.status(HttpStatus.UNPROCESSABLE_ENTITY).send({ message: err.message }); // 422 console.log(err); return res.status(HttpStatus.INTERNAL_SERVER_ERROR).send({ error: err, message: err.message }); } 

pool.js:

 'use strict'; const mysql = require('promise-mysql'); const pool = mysql.createPool({ connectionLimit: 100, host: 'localhost', user: 'user', password: 'password', database: 'database', charset: 'utf8mb4', debug: false }); module.exports = pool; 

errors.js:

 'use strict'; class ExtendableError extends Error { constructor(message) { if (new.target === ExtendableError) throw new TypeError('Abstract class "ExtendableError" cannot be instantiated directly.'); super(message); this.name = this.constructor.name; this.message = message; Error.captureStackTrace(this, this.contructor); } } // 400 Bad Request class BadRequest extends ExtendableError { constructor(m) { if (arguments.length === 0) super('bad request'); else super(m); } } // 401 Unauthorized class Unauthorized extends ExtendableError { constructor(m) { if (arguments.length === 0) super('unauthorized'); else super(m); } } // 403 Forbidden class Forbidden extends ExtendableError { constructor(m) { if (arguments.length === 0) super('forbidden'); else super(m); } } // 404 Not Found class NotFound extends ExtendableError { constructor(m) { if (arguments.length === 0) super('not found'); else super(m); } } // 409 Conflict class Conflict extends ExtendableError { constructor(m) { if (arguments.length === 0) super('conflict'); else super(m); } } // 422 Unprocessable Entity class UnprocessableEntity extends ExtendableError { constructor(m) { if (arguments.length === 0) super('unprocessable entity'); else super(m); } } // 500 Internal Server Error class InternalServerError extends ExtendableError { constructor(m) { if (arguments.length === 0) super('internal server error'); else super(m); } } module.exports.BadRequest = BadRequest; module.exports.Unauthorized = Unauthorized; module.exports.Forbidden = Forbidden; module.exports.NotFound = NotFound; module.exports.Conflict = Conflict; module.exports.UnprocessableEntity = UnprocessableEntity; module.exports.InternalServerError = InternalServerError; 

我想你可以做这样的事情。 无论如何,一旦完成查询,连接就会被释放,服务器也不会因为错误而崩溃。

 var queryString = "SELECT * FROM notification_detail nd LEFT JOIN notification n ON nd.id_notification = n.uuid WHERE login_id = ? id_company = ?;"; var filter = [loginId, idCompany]; var query = connection.query({ sql: queryString, timeout: 10000, }, filter ); query .on('error', function(err) { if (err) { console.log(err.code); // Do anything you want whenever there is an error. // throw err; } }) .on('result', function(row) { //Do something with your result. }) .on('end', function() { connection.release(); }); 

这可以是一个更简单的替代解决scheme。

 var query = connection.query({ sql: queryString, timeout: 10000, }, function(err, rows, fields) { if (err) { //Do not throw err as it will crash the server. console.log(err.code); } else { //Do anything with the query result } connection.release() });