Express篇-连接mysql
创建数据库配置文件config/sqlconfig.js
const sqlconfig = {host: 'localhost', // 连接地址user: 'root', //用户名password: '****', //密码port: 3306 , //端口号database: 'mysql01_dbbooks' //数据库名
}
module.exports = sqlconfig封装数据库管理工具 utils/mysqlUtils.js
连接数据库的两种简单方式:
使用mysql.createConnection连接数据库
使用连接池 pool.createPool()
const mysql = require('mysql')
const moment = require('moment')
// 加密模块中的随机生成数
const { randomInt } = require('crypto')
const sqlconfig = require('../config/sqlconfig')// 方式1 使用mysql.createConnection连接数据库
/* let mySql = (req,res,sql)=>{// console.log(sql);//连接数据库let conn= mysql.createConnection(sqlconfig)// 执行SQL语句conn.query(sql, (err, result)=>{// 错误信息if(err){console.log(err);return}console.log(result);res.send({message: '操作成功!',result})})
} */// 方式2 使用连接池 pool.createPool()
let pool = mysql.createPool(sqlconfig)
// 执行数据库
const exec2 = (sql,values) => {return new Promise((resolve, reject) => {pool.getConnection((err, conn) => {if (err) {//连接错误reject(err)} else {//连接成功// sql语句中使用?占位符, values传递是个数组conn.query(sql,values, (err, data) => {if (err) {//操作失败reject(err)} else {resolve({code: 0,message: '操作成功!',data,})// resolve(data)}})}// 当连接不再使用时,用conn对象的release方法将其归还到连接池中conn.release()})})
}
const exec = (sql) => {return new Promise((resolve, reject) => {pool.getConnection((err, conn) => {if (err) {//连接错误reject(err)} else {//连接成功conn.query(sql, (err, data) => {if (err) {//操作失败reject(err)} else {resolve({code: 0,message: '操作成功!',data,})// resolve(data)}})}// 当连接不再使用时,用conn对象的release方法将其归还到连接池中conn.release()})})
}// 查询
const searchList = (req, res, table = 'books') => {// 根据输入的查询条件查找数据 若无参数则查询所有let sql = `select * from ${table} where 1=1`let keys = Object.keys(req.query)let values = Object.values(req.query)let keyArr = []let valArr = []let str = ''let i = 0keys.forEach((item,index)=>{keyArr.push(item.trim())console.log(item.trim());valArr.push(`${values[index].trim()}`)str += ` and ${keyArr[i]} like '%${valArr[i]}%' `i++})sql += strexec(sql).then(result=>{res.send({code: 1,message: "查询成功!",result})})
}// 增加数据
// 封装对象的方法
const addObj = (obj,table) => {if(table === 'books'){let { isbn='',bookname,imgUrl='',publisher='',pubYear=null,pages=0,content='',price=0.0 } = obj;if (pubYear !== null) {pubYear = moment(pubYear,'YYYY-MM-DD').toDate();}// 随机生成 bid randomInt(x)返回0~x的随机整数const bid = moment().get() + '_' + (randomInt(9999999) + 1) + '';return Array.of(bid,isbn,bookname,imgUrl,publisher,pubYear,pages,content,price);}if(table === 'users'){let { nickname='',password,phone,email,uImg=null } = obj;// 随机生成 uidconst uid = moment().get() + '_' + (randomInt(9999999) + 1) + '';return Array.of(uid,nickname,password,phone,email,uImg);}
}const addList = (req, res, table = 'books') => {let { data } = req.bodylet values = []// data是个数组if(Array.isArray(data)){values = data.map(obj => addObj(obj,table))} else {// 对象values= addObj(data,table);}let sql = ''if(table === 'books'){// sql语句中 ? 为占位符, 表该处有内容, 具体是什么,后边传值, 所传数据(数组中的元素)的个数必须要和占位符的个数一样, 要以数组的形式进行传递(不论有几个元素)sql = ` insert into books(bid,isbn,bookname,imgUrl,publisher,pubYear,pages,content,price) values (?) `}if(table === 'users'){sql = ` insert into users(uid,nickname,password,phone,email,uImg) values (?) `}console.log("values:",values);// [values] 因为values本身是个数组,如果直接传递, 可能会有多个元素,与?个数不对应, [values]相当于定义数组, 故传递时,变成了二维数组exec2(sql, [values]).then(data => { res.send({ code: 1, message: "添加成功!",data });});
}// 更新数据 根据id更新
const updateList = (req, res, id, table = 'books', str='bid') => {const keys = Object.keys(req.body)const val = Object.values(req.body)let sql = ''let items = []keys.forEach((item, index) => {items.push(`${item} = '${val[index]}'`)})sql = `update ${table} set ${items} where ${str} = '${id}'`console.log(sql)// mySql(req, res, sql)exec(sql).then(result=>{res.send({code: 1,result})})
}// 删除
const deleteList = (req, res, table = 'books') => {const keys = Object.keys(req.body)const val = Object.values(req.body)console.log(keys);if (keys.length !== 0) {// 按理来说, 需要查询数据库中是否有该数据let sql = `delete from ${table} where 1=1`let str = ''keys.forEach((item, index) => {str += ` and ${item} like '%${val[index]}%' `})console.log(sql);// mySql(req, res, sql)// let searchSQL= `select * from ${table} where 1=1 ${str}`// exec(searchSQL).then(result=>{// })sql += strexec(sql).then(result=>{res.send({code: 1,message: "删除成功!",result})})} else {res.send({message: '删除条件为空!',})}
}const login = (req,res)=>{// 根据 phone / email 作为账号来登录 输入内容不为空, 判断是Email还是phone 根据对应信息查询数据, 如果能查到则登录成功, 否则登陆失败 let paramsArr = Object.values(req.body)// 有数据let flag = 0if(paramsArr && paramsArr.length > 0){// 遍历数组,并去空paramsArr.forEach(item => {if(item.trim().length === 0){flag++}})// 账号 密码存在if(flag === 0 && req.body.password.length > 0){let username = paramsArr.toString().indexOf('@') > 0 ? 'email' : 'phone'let sql = `select * from users where `if(username === 'email'){const {password , email } = req.body// 邮箱登录sql += ` password = '${ password }' and email = '${email}' `console.log(sql);// 查询数据, 查到则登录成功exec(sql).then(result=>{console.log(result.data);if(result.data.length > 0){res.send({code: 1,message: '登录成功!',result})}else{res.send({code: 0,message: '登录失败!'})}})}if(username === 'phone'){const {password , phone } = req.body// 邮箱登录sql += ` password = '${ password }' and phone = '${phone}' `console.log(sql);// 查询数据, 查到则登录成功exec(sql).then(result=>{// console.log(result);if(result.data.length > 0){res.send({code: 1,message: '登录成功!',result})}else{res.send({code: 0,message: '登录失败!'})}})}}else{// 账号或密码为空res.send({code: -1,message : "账号或密码不能为空!"})}}else{res.send({code: -1,message : "账号和密码不能为空!"})}
}module.exports = {searchList,addList,updateList,deleteList,login,
}
3.routes/user.js
var express = require('express');
const {searchList,addList,updateList,deleteList,login}= require('../utils/mysqlUtils')var router = express.Router();router.get('/searchList', function(req, res, next) {searchList(req,res,'users')
})
router.post('/addList', function(req, res, next) {addList(req,res,'users')
})router.post('/updateList', (req,res,next)=>{const {uid} = req.bodyif(uid && uid.trim() != '') {updateList(req,res,req.body.uid,'users','uid')}else{res.send({message: 'uid不能为空!!!'})}
})router.delete('/delList', (req,res,next)=>{// const {bid} = req.body// if(bid && bid.trim() != '') {deleteList(req,res,'users')// }else{// res.send({// message: 'bid不能为空!!!'// })// }
})router.post('/login',(req,res,next)=>{login(req,res)
})module.exports = router4.定时任务
在实际开发项目中,会遇到很多定时任务的工作。比如:定时导出某些数据、定时发送消息或邮件给用户、定时备份什么类型的文件等等。在nodejs中使用 node-schedule 完成定时任务。
安装:npm install node-schedule --save-dev
使用:
const schedule = require('node-schedule');
const scheduleCronstyle = ()=>{//每分钟的第30秒定时执行一次:schedule.scheduleJob('30 * * * * *',()=>{console.log('scheduleCronstyle:' + new Date());});
}
res.send(xss("<script> while(true){alert('111')}</script>"));举个栗子:每天凌晨2点0分0秒备份日志
const fs = require('fs');
const path = require('path');
const moment = require('moment');
server.listen(port, () => {// 添加定时器schedule.scheduleJob('0 0 2 * * *', () => {// 使用pipe,streamconst source = path.resolve(__dirname, '../logs', 'access.log');const target = path.resolve(__dirname, '../logs/bak', 'access.log' + moment().format('YYYYMMDDHHmmss') + '.bak');const rs = fs.createReadStream(source);const ws = fs.createWriteStream(target);rs.pipe(ws);ws.on('close', () => {console.log("备份完成!");});});
});5.MySQL安全问题
(1)sql注入:窃取数据
最原始,最简单的攻击手段。 攻击方式:输入一个sql片段(s0001' or s.sid like '%%),最终拼接成一段攻击代码。
// 关键点解释
const arr = [1,2,3];
const [a,b,c]=arr;// 数组的解构
console.log("a=",a,"b=",b,"c=",c);
const arr2 = [arr]; // 定义数组
console.log(arr2);
// 结果
// a= 1 b= 2 c= 3
// [ [ 1, 2, 3 ] ]
bookRouter.post('/modify', (req, res, next) => {
const { bookid, bookname, isbn, publishinghouse } = req.body;
let sql = ` UPDATE books b SET b.bid = b.bid `;
const params = [];
if (bookname && bookname.trim() !== '') {
sql += ` , b.bookname= ? `;
params.push(bookname);
}
if (isbn && isbn.trim() !== '') {
sql += `, b.isbn=? `;
params.push(isbn);
}
if (publishinghouse && publishinghouse.trim() !== '') {
sql += ` , b.publishinghouse=? `;
params.push(publishinghouse);
}
sql += ` WHERE b.bid = ? `;
// 有多个占位符,(UPDATE books b SET b.bid = b.bid , b.isbn=? WHERE b.bid = ? ),
// 在传参时,数据的个数必须要和占位符的个数一样,要以数组的形式进行传递
params.push(bookid);
console.log('paramsType:', Object.prototype.toString.call(params), params);
exec2(sql,params).then(data => {
res.send(JSON.stringify({ code: 0, data }));
});
});预防sql注入:
<1>使用mySql提供的escape函数
const escape = require('mysql').escape;
const sql = select * from users u where u.username=? and u.password=
${escape(password)} ;<2>使用占位符
const sql = ` select * from users u where u.username=? and u.password= ? `;
exec2(sql, [username,password]);预防xss攻击(xss攻击:窃取前端的cookie)
攻击方式:在页面展示内容中夹入js代码,以获取网页信息
预防措施:转换生成js的特殊字符1、安装xss: npm install xss -S
2、使用 xss(content);// content代表内容
res.send(xss("<script> while(true){alert('111')}</script>"));
源代码放在这里啦~~~,朋友们帮我提提下载量呗
https://download.csdn.net/download/qq_54379580/87442975
相关文章:
Express篇-连接mysql
创建数据库配置文件config/sqlconfig.jsconst sqlconfig {host: localhost, // 连接地址user: root, //用户名password: ****, //密码port: 3306 , //端口号database: mysql01_dbbooks //数据库名 } module.exports sqlconfig封装数据库管理工具 utils/mysqlUtils.…...
win10 安装rabbitMQ详细步骤
win10 安装rabbitMQ详细步骤 win10 安装rabbitMQ详细步骤win10 安装rabbitMQ详细步骤一、下载安装程序二、安装配置erlang三、安装rabbitMQ四、验证初始可以通过用户名:guest 密码guest来登录。报错:安装RabbitMQ出现Plugin configuration unchanged.问题…...
【成为架构师课程系列】一线架构师:6个经典困惑及其解法
目录 一线架构师:6个经典困惑及其解法 多阶段还是多视图? 内置最佳实践 架构方法论:3个阶段,一个贯穿 Pre-architecture阶段:ADMEMS矩阵方法 Conceptual Architecture阶段:重大需求塑造做概念架构 Refined Architecture…...
光耦合器的定义与概述
光耦合器或光电耦合器是一种电子元件,基本上充当具有不同电压电平的两个独立电路之间的接口。光耦合器是可在输入和输出源之间提供电气隔离的常用元件。它是一个 6 引脚器件,可以有任意数量的光电探测器。 在这里,光源发出的光束作为输入和输…...
谷粒商城--品牌管理详情
目录 1.简单上传测试 2.Aliyun Spring Boot OSS 3.模块mall-third-service 4.前端 5.数据校验 6.JSR303数据校验 7.分组校验功能 8.自定义校验功能 9.完善代码 1.简单上传测试 OSS是对象存储服务,有什么用呢?把图片存储到云服务器上能让所有人…...
stack、queue和priority_queue
目录 一、栈(stack) 1.stack的使用 2.容器适配器 3.stack的模拟实现 二、队列(queue) 1.queue的使用 2.queue的模拟实现 三、双端队列(deque) 1.vector,list的优缺点 2.认识deque 四…...
面试题(二十二)消息队列与搜索引擎
2. 消息队列 2.1 MQ有什么用? 参考答案 消息队列有很多使用场景,比较常见的有3个:解耦、异步、削峰。 解耦:传统的软件开发模式,各个模块之间相互调用,数据共享,每个模块都要时刻关注其他模…...
Spring Security in Action 第三章 SpringSecurity管理用户
本专栏将从基础开始,循序渐进,以实战为线索,逐步深入SpringSecurity相关知识相关知识,打造完整的SpringSecurity学习步骤,提升工程化编码能力和思维能力,写出高质量代码。希望大家都能够从中有所收获&#…...
Java面试——maven篇
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
基于微信小程序的游戏账号交易小程序
文末联系获取源码 开发语言:Java 框架:ssm JDK版本:JDK1.8 服务器:tomcat7 数据库:mysql 5.7/8.0 数据库工具:Navicat11 开发软件:eclipse/myeclipse/idea Maven包:Maven3.3.9 浏览器…...
Matlab绘制隐函数总结-二维和三维
1.二维隐函数 二维隐函数满足f(x,y)0f(x,y)0f(x,y)0,这里无法得到yf(x)yf(x)yf(x)的形式。不能通过普通函数绘制。 我们要关注的是使用fplot函数和fimplicit函数。 第1种情况:基本隐函数 基本的隐函数形式形如: x2y22x2(x2y2)12x^{2}y^{…...
如何直观地理解傅立叶变换?频域和时域的理解
如何直观地理解傅立叶变换 傅里叶变换连续形式的傅立叶变换如何直观地理解傅立叶变换?一、傅里叶级数1.1傅里叶级数的三角形式1.2 傅里叶级数的复指数形式二、傅里叶变换2.1一维连续傅里叶变换三、频谱和功率谱3.1频谱的获得3.2频谱图的特征3.3频谱图的组成频域(frequency do…...
STC15读取内部ID示例程序
STC15读取内部ID示例程序🎉本案例基于STC15F2K60S2为验证对象。 📑STC15 ID序列介绍 STC15系列STC最新一代STC15系列单片机出厂时都具有全球唯一身份证号码(ID号)。最新STC15系列单片机的程序存储器的最后7个字节单元的值是全球唯一ID号,用…...
Xml格式化与高亮显示
具体请参考:Xml格式化与高亮显示...
【GlobalMapper精品教程】045:空间分析工具(2)——相交
GlobalMapper提供的空间分析(操作)的方法有:交集、并集、单并集、差异、对称差集、相交、重叠、接触、包含、等于、内部、分离等,本文主要讲述相交工具的使用。 文章目录 一、实验数据二、符号化设置三、相交运算四、结果展示五、心灵感悟一、实验数据 加载配套实验数据(…...
4年外包终上岸,我只能说这类公司能不去就不去..
我大学学的是计算机专业,毕业的时候,对于找工作比较迷茫,也不知道当时怎么想的,一头就扎进了一家外包公司,一干就是4年。现在终于跳槽到了互联网公司了,我想说的是,但凡有点机会,千万…...
sklearn降维算法1 - 降维思想与PCA实现
目录1、概述1.1 维度概念2、PCA与SVD2.1 降维实现2.2 重要参数n_components2.2.1 案例:高维数据的可视化2.2.2 最大似然估计自选超参数2.2.3 按信息量占比选超参数1、概述 1.1 维度概念 shape返回的结果,几维几个方括号嵌套 特征矩阵特指二维的 一般来…...
「期末复习」线性代数
第一章 行列式 行列式是一个数,是一个结果三阶行列式的计算:主对角线的乘积全排列与对换逆序数为奇就为奇排列,逆序数为偶就为偶排列对换:定理一:一个排列的任意两个元素对换,排列改变奇偶性(和…...
伏并网低电压穿越技术
国内光伏并网低电压穿越要求 略: 低电压穿越方法 当前,光伏电站实现低电压穿越可通过两种方式,即增加硬件设备或者改变控制策略。本节对基于储能设备、基于无功补偿设备、基于无功电流电压支撑控制策略三种实现LVRT的典型方法进行介绍。 …...
opencv的环境搭建
大家好,我是csdn的博主:lqj_本人 这是我的个人博客主页: lqj_本人的博客_CSDN博客-微信小程序,前端,python领域博主lqj_本人擅长微信小程序,前端,python,等方面的知识https://blog.csdn.net/lbcyllqj?spm1011.2415.3001.5343哔哩哔哩欢迎关注…...
国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
解锁数据库简洁之道:FastAPI与SQLModel实战指南
在构建现代Web应用程序时,与数据库的交互无疑是核心环节。虽然传统的数据库操作方式(如直接编写SQL语句与psycopg2交互)赋予了我们精细的控制权,但在面对日益复杂的业务逻辑和快速迭代的需求时,这种方式的开发效率和可…...
linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...
VTK如何让部分单位不可见
最近遇到一个需求,需要让一个vtkDataSet中的部分单元不可见,查阅了一些资料大概有以下几种方式 1.通过颜色映射表来进行,是最正规的做法 vtkNew<vtkLookupTable> lut; //值为0不显示,主要是最后一个参数,透明度…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...
tomcat入门
1 tomcat 是什么 apache开发的web服务器可以为java web程序提供运行环境tomcat是一款高效,稳定,易于使用的web服务器tomcathttp服务器Servlet服务器 2 tomcat 目录介绍 -bin #存放tomcat的脚本 -conf #存放tomcat的配置文件 ---catalina.policy #to…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
全面解析数据库:从基础概念到前沿应用
在数字化时代,数据已成为企业和社会发展的核心资产,而数据库作为存储、管理和处理数据的关键工具,在各个领域发挥着举足轻重的作用。从电商平台的商品信息管理,到社交网络的用户数据存储,再到金融行业的交易记录处理&a…...
