本系列的目标快速上手 Express 框架, 基于官方文档归纳
参考中文文档:https://nodejs.cn/express/4x/api/
项目: https://github.com/eezd/cli-template/tree/main/nodejs-express-template
环境: Express 4.16
异常处理
我们再构建 Promise 异步数据库模块前, 请先跟我以前了解下具体的 异常处理 流程。
同步异常
这是一个很简单的同步异常处理,需要注意,路由需要在异常拦截器前面。
// routes/index.js
router.get("/", (req, res, next) => {
// 用户没有登录
let isLogin = false;
// 如果用户登录
if (isLogin) {
// 让请求继续向下执行
next();
} else {
// 如果用户没有登录 直接对客户端做出响应
// res.send("您还没有登录 不能访问/admin这个页面");
throw new Error("没有权限");
}
});
我们在 app.js
里建立一个异常拦截器, 拦截业务报错
// app.js
// error handler
// 这里是捕获异常, 对异常进行处理
app.use(function (err, req, res, next) {
// set locals, only providing error in development
// res.locals.message = err.message;
// res.locals.error = req.app.get("env") === "development" ? err : {};
// 存在 err.code 说明是业务异常, 设置 HTTP 状态码为 200
if (err.code) {
res.status(200);
} else {
res.status(err.status || 500);
}
res.status(err.status || 500);
res.json({ msg: err.message, code: err.code || 500 });
// res.render("error");
});
异步异常-定时器
必须要使用 next(error)
传递异常到拦截器上。
app.get("/", (req, res) => {
setTimeout(() => {
next(Object.assign(new Error("由定时器抛出的异步异常"), { code: 400 }));
}, 3000);
});
异步异常-Promise
- 要点一
- 必须使用
reject
标记失败, 不能直接掏出异常
- 必须使用
- 要点二
- 必须使用
try...catch
捕获异步异常
- 必须使用
- 要点三
- 捕获到后使用
next(error)
传递异常到拦截器上。
- 捕获到后使用
router.get("/", async (req, res, next) => {
function _login() {
return new Promise((resolve, reject) => {
// 使用 reject 标记 Promise 失败,并传递错误信息
reject(Object.assign(new Error("User not found"), { code: 400 }));
});
}
try {
await _login();
res.json({ title: "Express" });
} catch (error) {
// 捕获 _login 中抛出的错误
next(error);
}
});
express-async-errors
这个插件可以帮助我们无须使用 try...catch
去捕获 Promise
的异常
npm i express-async-errors
然后我们在 app 中引入
require("express-async-errors");
测试下
app.get("/", async (req, res, next) => {
function _login() {
return new Promise((resolve, reject) => {
// 使用 reject 标记 Promise 失败,并传递错误信息
reject(new Error("this is an error"));
});
}
await _login();
res.json({ title: "Express" });
});
Mysql 异步模块
- 没有设置
error handler
的话- 下面代码请使用
reject(err)
拦截异常
- 下面代码请使用
pnpm i mysql
const mysql = require("mysql");
const fs = require("fs");
const pool = mysql.createPool({
host: "127.0.0.1",
user: "root",
password: "root",
port: 3306,
database: "study",
ssl: {
ca: fs.readFileSync("./ca.pem"), // CA证书
key: fs.readFileSync("./client-key.pem"), // 客户端私钥
cert: fs.readFileSync("./client-cert.pem"), // 客户端证书
},
});
let query = function (sql, values = []) {
return new Promise((resolve, reject) => {
pool.getConnection(function (err, connection) {
if (err) {
// reject(err);
reject(Object.assign(new Error(err.sqlMessage), { code: 400 }));
} else {
connection.query(sql, values, (err, rows) => {
if (err) {
// reject(err);
reject(Object.assign(new Error(err.sqlMessage), { code: 400 }));
} else {
resolve(rows);
}
connection.release();
});
}
});
});
};
module.exports = { query };
- 调用
- 假如已启用了
express-async-errors
模块, 则可以不需要try...catch
捕获。
- 假如已启用了
var express = require("express");
var router = express.Router();
var mysql = require("../mysql");
/* GET home page. */
router.get("/", async (req, res, next) => {
try {
let query_data = await mysql
.query("SELECT * FROM tbd1 WHERE id = ? and price = ? ", [5, 1349])
.then((res) => {
return res;
});
res.json(query_data);
} catch (error) {
// 捕获 _login 中抛出的错误
next(error);
}
});
module.exports = router;
sqlite3
- 没有设置
error handler
的话- 下面代码请使用
reject(err)
拦截异常
- 下面代码请使用
pnpm i sqlite3
const sqlite3 = require("sqlite3").verbose();
var path = require("path");
const dbPath = path.join(__dirname, "./sqlite.db");
function all(sql, params = []) {
return new Promise((resolve, reject) => {
const db = new sqlite3.Database(dbPath);
db.on("error", (err) => {
// reject(err);
reject(Object.assign(new Error(err.message), { code: 400 }));
});
db.all(sql, params, (err, rows) => {
if (err) {
// reject(err);
reject(Object.assign(new Error(err.message), { code: 400 }));
} else {
resolve(rows);
}
db.close();
});
});
}
function get(sql, params = []) {
return new Promise((resolve, reject) => {
const db = new sqlite3.Database(dbPath);
db.on("error", (err) => {
// reject(err);
reject(Object.assign(new Error(err.message), { code: 400 }));
});
db.get(sql, params, (err, rows) => {
if (err) {
// reject(err);
reject(Object.assign(new Error(err.message), { code: 400 }));
} else {
resolve(rows);
}
db.close();
});
});
}
function run(sql, params = []) {
return new Promise((resolve, reject) => {
const db = new sqlite3.Database(dbPath);
db.on("error", (err) => {
// reject(err);
reject(Object.assign(new Error(err.message), { code: 400 }));
});
db.run(sql, params, function (err, rows) {
if (err) {
// reject(err);
reject(Object.assign(new Error(err.message), { code: 400 }));
} else {
// 插入成功
resolve({ lastID: this.lastID, changes: this.changes });
}
db.close();
});
});
}
module.exports = {
get,
all,
run,
};
- 调用
- **假如已启用了
express-async-errors
模块, 则可以不需要try...catch
捕获。
- **假如已启用了
var express = require("express");
var router = express.Router();
var db = require("../sqlite");
/* GET home page. */
router.get("/", async (req, res, next) => {
try {
let query_data = await db.all("SELECT * FROM user").then((res) => {
return res;
});
res.json(query_data);
} catch (error) {
next(error);
}
});
module.exports = router;