(一)未优化前的代码
实现步骤
一、获取客户端提交到服务器的用户信息,对表单中的数据,进行合法性的效验 代码如下:
// 注册新用户的接口
exports.regUser = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userinfo = req.body;
// 对表单中的数据,进行合法性的效验,如果有一项为空
if (!userinfo.username || !userinfo.password) {
return res.send({
status: 1,
message: "用户名或密码不合法",
});
}
console.log(userinfo);
};
二、检测用户名是否被占用
/utils/db.js文件的代码如下:
const mysql = require("mysql");
const db = mysql.createPool({
host: "localhost", // 连接地址
port: "3306", //端口号
user: "root", //用户名XXXXXXXX
password: "XXXXXXXX", //密码
database: "exapp2", //数据库名
});
module.exports = db;
var db = require("../utils/db");
// 注册新用户的接口
exports.regUser = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userinfo = req.body;
// 对表单中的数据,进行合法性的效验,如果有一项为空
if (!userinfo.username || !userinfo.password) {
return res.send({
status: 1,
message: "用户名或密码不合法",
});
}
// 检验用户名是否被占用
// 定义sql语句,查询用户名是否被占用
var sql = `select * from ev_users where username=?`;
db.query(sql, userinfo.username, (err, data) => {
// 执行sql语句失败,
if (err) {
return res.send({
status: 1,
message: err.message,
});
}
// 判断用户名是否被占用,data.length 大于0说明sql语句查到了该用户名,所以已定被占用
if (data.length > 0) {
// console.log("用户名被占用吗,请更换其他用户名");
return res.send({
status: 1,
message: "用户名被占用吗,请更换其他用户名",
});
}
// res.send("ok");
});
console.log(userinfo);
};
三、对密码进行加密
项目安装指定版本bcryptjs库
npm i bcryptjs@2.4.3
var db = require("../utils/db");
// 导入密码加密
const bcrypt = require("bcryptjs");
// 注册新用户的接口
exports.regUser = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userinfo = req.body;
// 对表单中的数据,进行合法性的效验,如果有一项为空
if (!userinfo.username || !userinfo.password) {
return res.send({
status: 1,
message: "用户名或密码不合法",
});
}
// 检验用户名是否被占用
// 定义sql语句,查询用户名是否被占用
var sql = `select * from ev_users where username=?`;
db.query(sql, userinfo.username, (err, data) => {
// 执行sql语句失败,
if (err) {
return res.send({
status: 1,
message: err.message,
});
}
// 判断用户名是否被占用,data.length 大于0说明sql语句查到了该用户名,所以已定被占用
if (data.length > 0) {
// console.log("用户名被占用吗,请更换其他用户名");
return res.send({
status: 1,
message: "用户名被占用吗,请更换其他用户名",
});
}
// 用户名可以使用,对密码进行加密
// bcrypt.hashSync(客户端明文密码,10) // 第二个参数提高密码的安全性
userinfo.password = bcrypt.hashSync(userinfo.password, 10);
//console.log("@", userinfo.password);
// res.send("ok");
});
console.log(userinfo);
};
四、插入新用户(完整代码)
var db = require("../utils/db");
// 导入密码加密
const bcrypt = require("bcryptjs");
// 注册新用户的接口
exports.regUser = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userinfo = req.body;
// 1.对表单中的数据,进行合法性的效验,如果有一项为空
if (!userinfo.username || !userinfo.password) {
return res.send({
status: 1,
message: "用户名或密码不合法",
});
}
// 2.检验用户名是否被占用
// 定义sql语句,查询用户名是否被占用
var sql = `select * from ev_users where username=?`;
db.query(sql, userinfo.username, (err, data) => {
// 执行sql语句失败,
if (err) {
return res.send({
status: 1,
message: err.message,
});
}
// 判断用户名是否被占用,data.length 大于0说明sql语句查到了该用户名,所以已定被占用
if (data.length > 0) {
// console.log("用户名被占用吗,请更换其他用户名");
return res.send({
status: 1,
message: "用户名被占用吗,请更换其他用户名",
});
}
// 3.用户名可以使用,对密码进行加密
// bcrypt.hashSync(客户端明文密码,10) // 第二个参数提高密码的安全性
userinfo.password = bcrypt.hashSync(userinfo.password, 10);
// 4.插入新用户
// 定义插入新用户的sql语句
let sql1 = "insert into ev_users set ?";
// 调用db.query()执行sql语句
db.query(
sql1,
{ username: userinfo.username, password: userinfo.password,},
function (err, data) {
// 判断sql语句是否执行成功
if (err) {
return res.send({ status: 1, message: err.message });
}
// 判断影响行数是否为1
if (data.affectedRows !== 1) {
return res.send({ status: 1, message: "注册用户失败,请稍后再试" });
}
//注册成功
res.send({ status: 0, message: "注册成功!" });
}
);
// res.send("ok");
});
console.log(userinfo);
};
(二)优化后的代码
1. 在上面的代码的基础上封装res.send()方法为统一的中间件,在app.js文件中
// 一定要在路由之前封装res.cc函数
app.use((req, res, next) => {
// status 默认值 1,表示失败的情况
// err的值,可能时一个错误对象,也可能时以个错误的描述字符串
res.cc = function (err, status = 1) {
res.send({
status,
message: err instanceof Error ? err.message : err,
});
};
next();
});
然后在注册接口中把res.send修改成统一res.cc,代码如下:
var db = require("../utils/db");
// 导入密码加密
const bcrypt = require("bcryptjs");
// res.cc是在app.js封装的中间件
// 注册新用户的接口
exports.regUser = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userinfo = req.body;
// 对表单中的数据,进行合法性的效验,如果有一项为空
if (!userinfo.username || !userinfo.password) {
return res.send({
status: 1,
message: "用户名或密码不合法",
});
}
// 检验用户名是否被占用
// 定义sql语句,查询用户名是否被占用
var sql = `select * from ev_users where username=?`;
db.query(sql, userinfo.username, (err, data) => {
// 执行sql语句失败,
if (err) {
// return res.send({
// status: 1,
// message: err.message,
// });
return res.cc(err);
}
// 判断用户名是否被占用,data.length 大于0说明sql语句查到了该用户名,所以已定被占用
if (data.length > 0) {
// console.log("用户名被占用吗,请更换其他用户名");
// return res.send({
// status: 1,
// message: "用户名被占用吗,请更换其他用户名",
// });
return res.cc("用户名被占用吗,请更换其他用户名");
}
// 用户名可以使用,对密码进行加密
// console.log("@@", userinfo.password);
// bcrypt.hashSync(客户端明文密码,10) // 第二个参数提高密码的安全性
userinfo.password = bcrypt.hashSync(userinfo.password, 10);
console.log("@", userinfo.password);
// 定义插入新用户的sql语句
let sql1 = "insert into ev_users set ?";
// const sqlNewUser = "insert into ev_users set ?";
// 调用db.query()执行sql语句
db.query(
sql1,
{ username: userinfo.username, password: userinfo.password },
function (err, data) {
// 判断sql语句是否执行成功
if (err) {
// return res.send({ status: 1, message: err.message });
return res.cc(err);
}
// 判断影响行数是否为1
if (data.affectedRows !== 1) {
// return res.send({ status: 1, message: "注册用户失败,请稍后再试" });
return res.cc("注册用户失败,请稍后再试");
}
//注册成功
res.cc("注册成功", 0);
// res.send({ status: 0, message: "注册成功!" });
}
);
// res.send("ok");
});
console.log(userinfo);
};
2. 优化username,password 表单数据验证
在实际开发中,前后端都需要对表单的数据进行合法性的验证,而且,后端做为数据合法性验证的最后一个关口,在拦截非法数据方面,起到了至关重要的作用。
单纯的便用 if…else… 的形式对数据合法性进行验证,效率低下、出错率高、维护性差。因此,推荐便用第三方数据验证模块,来降低出错率、提高验证的效率与可维护性,让后端程序员把更多的精力放在核心业务逻辑的处理上.
(1).安装 jOi 包,为表单中携带的每个数据项,定义验证规则:
npm install joi
(2).安装 @escook/express-j01 中间件,来实现自动对表单数据进行验证的功能:
npm install @escook/express-joi
(3).新建schema/user.js
// 导入定义验证规则的包
// const joi = require("@hapi/joi");
const joi = require("joi");
/**
* string()值必须是字符串
* alphanum()值只能包含a-zA-ZO-9的字符串
* min(length) 最小长度
* max(length) 大长度
* required() 值是必填项,不能为 undefined
* pattern(正则表达式) 值必须符合正则表达式的规则
*/
// 定义用户名和密码的验证规则
const username = joi.string().alphanum().min(1).max(10).required();
const password = joi
.string()
.pattern(/^[\S]{6,12}$/)
.required();
// 定义验证注册和登录表单数据的规则对象
exports.reg_login_schema = {
body: {
username,
password,
},
};
(4).在routes/use.js中引入schema/user.js中的方法reg_login_schema,代码如下:
var express = require("express");
var router = express.Router();
// 引入封装的获取验证码的方法
var unr_handler = require("../controllers/user");
// 1.导入验证数据的中间件
const expressJoi =require('@escook/express-joi')
// 2.导入需要的验证规则对象
const { reg_login_schema } = require('../schema/user')
// 新用户注册
router.post("/regUser",expressJoi(reg_login_schema), unr_handler.regUser);
// router.post("/regUser", unr_handler.regUser);
// 登录
// router.post("/login", unr_handler.login);
module.exports = router;
(5)在 app.js 的全局错误级别中间件中,捕获验证失败的错误,并把验证失败的结果响应给客户端:
const joi = require('joi')
// 定义表单验证失败的错误的中间件,并把验证失败的结果响应给客户端
// 错误中间件
app.use(function (err, req, res, next) {
// 数据验证失败,instanceof判断
if (err instanceof joi.ValidationError) {
return res.cc(err);
}
// 未知错误
res.cc(err)
});
(6)最终优化后注册接口代码如下:
var db = require("../utils/db");
// 导入密码加密
const bcrypt = require("bcryptjs");
// res.cc是在app.js封装的中间件
// 注册新用户的接口
exports.regUser = (req, res) => {
// 获取客户端提交到服务器的用户信息
const userinfo = req.body;
// 对表单中的数据,进行合法性的效验,如果有一项为空
// if (!userinfo.username || !userinfo.password) {
// return res.send({
// status: 1,
// message: "用户名或密码不合法",
// });
// }
// 检验用户名是否被占用
// 定义sql语句,查询用户名是否被占用
var sql = `select * from ev_users where username=?`;
db.query(sql, userinfo.username, (err, data) => {
// 执行sql语句失败,
if (err) {
// return res.send({
// status: 1,
// message: err.message,
// });
return res.cc(err);
}
// 判断用户名是否被占用,data.length 大于0说明sql语句查到了该用户名,所以已定被占用
if (data.length > 0) {
// console.log("用户名被占用吗,请更换其他用户名");
// return res.send({
// status: 1,
// message: "用户名被占用吗,请更换其他用户名",
// });
return res.cc("用户名被占用吗,请更换其他用户名");
}
// 用户名可以使用,对密码进行加密
// console.log("@@", userinfo.password);
// bcrypt.hashSync(客户端明文密码,10) // 第二个参数提高密码的安全性
userinfo.password = bcrypt.hashSync(userinfo.password, 10);
console.log("@", userinfo.password);
// 定义插入新用户的sql语句
let sql1 = "insert into ev_users set ?";
// const sqlNewUser = "insert into ev_users set ?";
// 调用db.query()执行sql语句
db.query(
sql1,
{ username: userinfo.username, password: userinfo.password },
function (err, data) {
// 判断sql语句是否执行成功
if (err) {
// return res.send({ status: 1, message: err.message });
return res.cc(err);
}
// 判断影响行数是否为1
if (data.affectedRows !== 1) {
// return res.send({ status: 1, message: "注册用户失败,请稍后再试" });
return res.cc("注册用户失败,请稍后再试");
}
//注册成功
res.cc("注册成功", 0);
// res.send({ status: 0, message: "注册成功!" });
}
);
// res.send("ok");
});
console.log(userinfo);
};