>

node.js에서 mysql을 사용하는 몇 가지 예를 읽었으며 오류 처리에 대한 질문이 있습니다.

대부분의 예제는 다음과 같이 오류 처리를 수행합니다 (간단하게).

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 에서  블록. 그렇지 않으면 시간이 지남에 따라 연결이 열린 상태로 유지 될 수 있습니다.

저는 자바의 try...catch...finally 에 익숙합니다  또는 try-with-resources  여기서 오류를 "깨끗하게"잡아서 모든 자원을 닫을 수 있습니다. 오류를 전파하고 한 곳에서 모두 처리하는 방법이 있습니까?

  • 답변 # 1

    노드 7이 지원하는 es2016으로 변환하기 위해 es2017 구문과 Babel을 사용하여 처리하기로 결정했습니다.

    최신 버전의 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 사용 / await  이 try { try { } finally { } } catch { } pattern 와 함께  한 곳에서 모든 오류를 수집하고 처리 할 수있는 깔끔한 오류 처리 기능을 제공합니다. finally 블록은 무엇이든 데이터베이스 연결을 닫습니다.

    당신은 약속을 끝까지 다루고 있는지 확인해야합니다. 데이터베이스 액세스를 위해 promise-mysql 를 사용합니다.  일반 mysql 대신 모듈  구성 단위. 다른 모든 것에는 bluebird 를 사용합니다.  모듈과 promisifyAll() .

    또한 특정 상황에서 던질 수 있고 catch 블록에서 감지 할 수있는 사용자 정의 예외 클래스도 있습니다. 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;
    
    

  • 답변 # 2

    또 다른 우아한 솔루션은 async.series 를 사용하는 것입니다 및 오류 관리 방법

    const mysql = require('mysql') 
    const async = require('async')
    async.series([
      function (next) {
        db = mysql.createConnection(DB_INFO)
        db.connect(function(err) {
          if (err) {
            // this callback/next function takes 2 optional parameters: 
            // (error, results)
            next('Error connecting: ' + err.message)
          } else {
            next() // no error parameter filled => no error
          }
        })
      },
      function (next) {
         var myQuery = ....
         db.query(myQuery, function (err, results, fields) {
           if (err) {
             next('error making the query: ' + err.message)
             return // this must be here
           }
           // do something with results
           // ...
           next(null, results) // send the results
         })
       },
       function (next) {
         db.close()
       }], 
       //done after all functions were executed, except if it was an error 
       function(err, results) {
         if (err) {
           console.log('There was an error: ', err)
         }
         else {
           //read the results after everything went well
           ... results ....
         }
       })
    
    

  • 답변 # 3

    이러한 일을 할 수 있다고 생각합니다. 어쨌든 연결이 쿼리되면 연결이 해제되고 오류로 인해 서버가 중단되지 않습니다.

    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();
    });
    
    

    이것은 훨씬 간단한 대안 솔루션이 될 수 있습니다.

    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()
    });
    
    

  • 답변 # 4

    MySQL 연결에 성공하면 사용 가능한 풀을 반환하는 기능입니다. 따라서 쿼리를 진행하기 전에이 기능을 기다렸다가 연결이 올바른지 확인합니다. MySQL에 연결되어 있지 않아도 서버가 중단되지 않습니다.

    connect: function ()
        {
            return new Promise((resolve, reject) => {
                let pool = Mysql.createPool({
                    connectionLimit: config.mysql.connectionLimit,
                    host: config.mysql.host,
                    user: config.mysql.user,
                    password: config.mysql.password,
                    database: config.mysql.database
                });
                pool.getConnection((err, con) =>
                {
                    try
                    {
                        if (con)
                        {
                            con.release();
                            resolve({"status":"success", "message":"MySQL connected.", "con":pool});
                        }
                    }
                    catch (err)
                    {
                        reject({"status":"failed", "error":`MySQL error. ${err}`});
                    }
                    resolve({"status":"failed", "error":"Error connecting to MySQL."});
                });
            });
        }
    
    

    사용 된 MySQL 패키지 : https://www.npmjs.com/package/mysql

    기본 약속 async/await ES2017

  • 답변 # 5

    SQL 연결에서 반환 된 특정 오류 처리 사례를 처리하기 위해 콜백에서 반환 된 '오류'개체를 확인할 수 있습니다.

    그래 ..

    const mysql = require('mysql') 
    let conn = mysql.createConnection(connConfig)
    conn.query(query, function(error, result, fields){
        if (error){
            console.log(typeof(error));
            for(var k in error){
                console.log(`${k}: ${error[k]}`)
            }
    }
    
    

    위의 for 루프에있는 console.log 문은 다음과 같이 출력됩니다 :

    개체

    code: ER_TABLE_EXISTS_ERROR
    errno: 1050
    sqlMessage: Table 'table1' already exists
    sqlState: 42S01
    index: 0
    sql: CREATE TABLE table1 (
    PersonID int,
    LastName varchar(255),
    FirstName varchar(255),
    City varchar(255)
    );
    
    

    이 키를 사용하면 값을 핸들러로 전달할 수 있습니다

관련 자료

  • 이전 Angular 7에서 [hidden]과 [classhide]의 차이점은 무엇입니까?
  • 다음 python - Pylint 결과를 얻지 못하는 소나