Node.js+Express+Koa2开发接口学习笔记(三)

发布时间 2023-12-26 21:52:27作者: 小风车吱呀转

数据库操作(创建和增删查)

  • 使用Navicat快速创建myblog数据库
  • 创建表

pi1XR6s.png
pi1X2lj.png

使用navicat快速建表

pi0y6bV.png

pi0y429.png

  • 使用sql语句进行简单的查询
-- show tables; -- 显示该数据库中的所有表 

INSERT INTO users(username,`password`,realname) VALUES('zhangsan','123','张三') 
INSERT INTO users(username,`password`,realname) VALUES('lisi','123','李四') 
-- 往users表添加数据,其中`password`由于password是mysql的关键字所以需要加``

SELECT * FROM users -- 查询users的所有数据的所有列
SELECT id,username from users -- 查询users的所有数据的id和username列

SELECT * FROM users WHERE username = 'zhangsan' and `password` = '123' -- 使用where查询符合条件的数据,可以使用and 或者 or对多条件进行拼接

SELECT * FROM users WHERE username like '%zhang%' -- 使用like进行模糊查询

SELECT * FROM users WHERE username like '%zhang%' ORDER BY id;-- ORDER BY用于排序,默认是升序,如果是order by id DESC  则是根据id倒序

UPDATE users set realname = '李四2' WHERE username = 'lisi' -- update 更新语句,通常会和where一起使用
DELETE from users where username = 'lisi'; -- DELETE 删除语句,通常会和where一起使用,否则会把表中的所有数据删除,实际业务不会使用该语句进行删除,而是在表中添加一个删除标志进行软删除,好处是可以数据恢复
UPDATE users SET state = 0 WHERE username = 'lisi' -- 在表中添加一个删除标志 1为存在 0为删除
select * from users WHERE state = 1 -- 查询表中未被删除的所有数据 =1 可以写成 <> 0
SELECT VERSION() -- 可以查询mysql版本

Nodejs操作Mysql

安装插件

npm i mysql

编写代码

const mysql = require("mysql");

// 创建mysql连接
const con = mysql.createConnection({
  host: "localhost", 
  user: "root",
  password: "123456",
  port: 3306,
  database: "myblog",
});

// 开始连接
con.connect();
// 执行sql语句
// const sql = "select * from users;";
// const sql = `update users set realname = '李四2' where username = 'lisi'`;
const sql = `INSERT INTO blogs(title,content,createtime,author) VALUES ('标题C','内容C',1700988977111,'钱五')`;
con.query(sql, (err, result) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log("result", result);
});
// 关闭连接
con.end();

nodejs链接mysql做成工具

上述代码仅仅是一个demo,如果需要放入项目中,由于开发环境和线上环境的mysql配置不一样,我们需要将上面代码封装成一个工具文件,保证开发环境和线上环境都可以适应使用。

创建conf文件夹,在里面创建db.js文件用来获取当前环境的mysql配置。

const env = process.env.NODE_ENV; //环境变量

// 配置
let MYSQL_CONF;

if (env === "dev") {
  MYSQL_CONF = {
    host: "localhost",
    user: "root",
    password: "123456",
    port: 3306,
    database: "myblog",
  };
}

if (env === "production") {
  MYSQL_CONF = {
    host: "localhost",
    user: "root",
    password: "123456",
    port: 3306,
    database: "myblog",
  };
}

module.exports = {
  MYSQL_CONF,
};

在package.json中,我们配置了dev和prod命令用来开发运行和线上运行,通过nodejs的process可以获取到程序进程的环境变量为开发环境还是为线上环境。

"dev": "cross-env NODE_ENV=dev nodemon ./bin/www.js",
"prod": "cross-env NODE_ENV=production nodemon ./bin/www.js"

接着创建db文件夹,在里面创建mysql.js用来创建链接mysql的对象,并对外提供一个可以统一执行sql的函数

const mysql = require("mysql");
const { MYSQL_CONF } = require("../conf/db");

// 创建链接对象
const con = mysql.createConnection(MYSQL_CONF);

// 开始链接
con.connect();

// 统一执行sql的函数
function exec(sql) {
  const promise = new Promise((resolve, reject) => {
    con.query(sql, (err, result) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(result);
    });
  });
  return promise;
}
module.exports = {
  exec,
};

API对接mysql(博客列表)

链接了数据库后,之前API返回的假数据都可以通过mysql查询语句返回结果,需要注意的是封装的sql执行函数返回的结果是一个promise对象,所以在controller和router文件夹都需要进行改动。

controller->blog.js

const { exec } = require("../db/mysql");

const getList = (author, keyword) => {
  let sql = `select * from blogs where 1=1 `;
  if (author) {
    sql += `and author='${author}' `;
  }
  if (keyword) {
    sql += `and title like '%%${keyword}%' `;
  }
  sql += `order by createtime desc`;
  // 返回promise
  return exec(sql);
};

router->blog.js

  // 获取博客列表
  if (method === "GET" && req.path === "/api/blog/list") {
    const author = req.query.author || "";
    const keyword = req.query.keyword || "";
    // const listData = getList(author, keyword);
    // return new SuccessModel(listData);
    const result = getList(author, keyword);
    return result.then((listData) => {
      return new SuccessModel(listData);
    });
  }

由于路由文件返回的也是个promise对象,所以在app,js也需要对返回的数据进行处理

const serverHandle = (req, res) => {
  // 设置返回格式 JSON
  res.setHeader("Content-type", "application/json");

  // 获取path
  const url = req.url;
  req.path = url.split("?")[0];

  // 解析query
  req.query = querystring.parse(url.split("?")[1]);

  //处理post data
  getPostData(req).then((postData) => {
    req.body = postData;
    // 处理blob路由
    // const blogData = handleBlogRouter(req, res);
    // if (blogData) {
    //   res.end(JSON.stringify(blogData));
    //   return;
    // }

    const blogResult = handleBlogRouter(req, res);
    if (blogResult) {
      blogResult.then((blogData) => {
        res.end(JSON.stringify(blogData));
      });
      return;
    }

    // 处理user路由
    const userData = handleUserRouter(req, res);
    if (userData) {
      res.end(JSON.stringify(userData));
      return;
    }
    // 未命中路由,返回404,同时将返回格式修改为纯文本
    res.writeHead(404, { "Content-type": "text/plain" });
    res.write("404 NOT FOUND\n");
    res.end();
  });
};

此时访问服务器http://localhost:8000/api/blog/list可以看到查询列表成功

API对接mysql(博客详情和新建)

获取博客详情: http://localhost:8000/api/blog/detail?id=2

// controller->blog.js
const getDetail = (id) => {
  const sql = `select * from blogs where id ='${id}' `;
  return exec(sql).then((rows) => {
    return rows[0];
  });
};

//router->blog.js
// 获取博客详情
if (method === "GET" && req.path === "/api/blog/detail") {
  // const detailData = getDetail(id);
  // return new SuccessModel(detailData);
  const result = getDetail(id);
  return result.then((data) => {
    return new SuccessModel(data);
  });
}

新建博客:

//controller->blog.js
const newBlog = (blogData = {}) => {
  // blogData是一个博客对象,包含title content author属性
  const title = blogData.title;
  const content = blogData.content;
  const author = blogData.author;
  const createTime = Date.now();

  const sql = `
    insert into blogs (title,content,createtime,author)
    values ('${title}','${content}',${createTime},'${author}');
  `;
  return exec(sql).then((insertData) => {
    console.log("insertData is ", insertData);
    return {
      id: insertData.insertId,
    };
  });
};

//router->blog.js
// 新建一篇博客
if (method === "POST" && req.path === "/api/blog/new") {
  // const data = newBlog(req.body);
  // return new SuccessModel(data);
  const author = "zhangsan"; // 假数据,待开发登录时再改成真实数据
  req.body.author = author;
  const result = newBlog(req.body);
  return result.then((data) => {
     return new SuccessModel(data);
  });
}

打开postman或者其他API调试工具,http://localhost:8000/api/blog/new,创建新博客。

API对接mysql(博客更新和删除)

更新博客:http://localhost:8000/api/blog/update?id=3 填写body参数

//controller->blog.js
const updateBlog = (id, blogData = {}) => {
  // id就是要更新博客的id
  // blogData是一个博客对象,包含title content属性
  const title = blogData.title;
  const content = blogData.content;
  const sql = `
    update blogs set title='${title}',content='${content}' where id=${id}
  `;
  return exec(sql).then((updateData) => {
    if (updateData.affectedRows > 0) {
      return true;
    }
    return false;
  });
};

//router->blog.js
// 更新一篇博客
if (method === "POST" && req.path === "/api/blog/update") {
   const result = updateBlog(id, req.body);
   return result.then((val) => {
     if (val) {
       return new SuccessModel("更新博客成功");
     } else {
       return new ErrorModel("更新博客失败");
     }
});

删除博客:http://localhost:8000/api/blog/del?id=8&&author=zhangsan

传入author参数是为了确保别人无法删除其他人的文章

//controller->blog.js
const delBlog = (id, author) => {
  // id 就是要删除博客的id
  const sql = `
    delete from blogs where id=${id} and author = '${author}'
  `;
  return exec(sql).then((delData) => {
    if (delData.affectedRows > 0) {
      return true;
    }
    return false;
  });
};
//router->blog.js
// 删除博客
if (method === "POST" && req.path === "/api/blog/del") {
   const author = "zhangsan"; // 假数据,待开发登录时再改成真实数据
   const result = delBlog(id, author);
   console.log("result", result);
   return result.then((val) => {
     if (val) {
       return new SuccessModel();
     } else {
        return new ErrorModel("删除博客失败");
     }
   });
}

API对接mysql(登录)

controller->user.js

const { exec } = require("../db/mysql");
const loginCheck = ({ username, password }) => {
  const sql = `
    select username,realname from users where username='${username}' and password='${password}'
  `;
  return exec(sql).then((rows) => {
    return rows[0] || {};
  });
};
module.exports = {
  loginCheck,
};

router->user.js

const { loginCheck } = require("../controller/user");
const { SuccessModel, ErrorModel } = require("../model/resModel");
const handleUserRouter = (req, res) => {
  const method = req.method; // GET POST

  // 登录
  if (method === "POST" && req.path === "/api/user/login") {
    const result = loginCheck(req.body);
    return result.then((data) => {
      if (data.username) {
        return new SuccessModel();
      }
      return new ErrorModel("登录失败");
    });
  }
};
module.exports = handleUserRouter;

同样地还需要修改app.js里对于user路由的处理

    // 处理user路由
    // const userData = handleUserRouter(req, res);
    // if (userData) {
    //   res.end(JSON.stringify(userData));
    //   return;
    // }

    const userResult = handleUserRouter(req, res);
    if (userResult) {
      userResult.then((userData) => {
        res.end(JSON.stringify(userData));
      });
      return;
    }

总结

①使用nodejs链接mysql,执行sql语句

②根据NODE_ENV区分当前环境的mysql配置

③封装exec函数,APi使用exec函数操作数据库

源码:https://github.com/DaneOvO/Nodejs-Express-Koa2-Learning/tree/learning-three