Nodejs Express4 入门实战笔记02 请求req与响应res及Cookie

本文含有: Express 请求数据获取 params query body 响应数据 json send 重定向 render Cookie的使用

本系列的目标快速上手 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;
Licensed under CC BY-NC-SA 4.0
本博客已稳定运行
发表了53篇文章 · 总计28.17k字
使用 Hugo 构建
主题 StackJimmy 设计