【Node.js实战】一文带你开发博客项目之Koa2重构(实现session、开发路由、联调、日志)
个人简介
👀个人主页: 前端杂货铺
🙋♂️学习方向: 主攻前端方向,也会涉及到服务端
📃个人状态: 在校大学生一枚,已拿多个前端 offer(秋招)
🚀未来打算: 为中国的工业软件事业效力n年
🥇推荐学习:🍍前端面试宝典 🍉Vue2 🍋Vue3 🍓Vue2&Vue3项目实战 🥝Node.js🍒Three.js
🌕个人推广:每篇文章最下方都有加入方式,旨在交流学习&资源分享,快加入进来吧
Node.js系列文章目录
内容 | 参考链接 |
---|---|
Node.js(一) | 初识 Node.js |
Node.js(二) | Node.js——开发博客项目之接口 |
Node.js(三) | Node.js——一文带你开发博客项目(使用假数据处理) |
Node.js(四) | Node.js——开发博客项目之MySQL基础 |
Node.js(五) | Node.js——开发博客项目之API对接MySQL |
Node.js(六) | Node.js——开发博客项目之登录(前置知识) |
Node.js(七) | Node.js——开发博客项目之登录(对接完毕) |
Node.js(八) | Node.js——开发开发博客项目之联调 |
Node.js(九) | Node.js——开发博客项目之日志 |
Node.js(十) | Node.js——开发博客项目之安全 |
Node.js(十 一) | Node.js——开发博客项目之初识 Express |
Node.js(十二) | Node.js——开发博客项目之 Express 重构 |
文章目录
- Node.js系列文章目录
- 一、前言
- 二、实现 session
- 三、开发路由
- 1、安装 mysql 和 xss
- 2、代码迁移
- 3、修改 controller 控制器
- 4、开发路由
- 四、联调
- 五、日志
- 六、写在最后
一、前言
前面我们介绍了 await / async 的基本使用,学到了 koa2 框架的安装、项目的创建,以及路由的基本使用。
接下来,我们正式使用 koa2 对我们的 myblog 博客项目进行重构!
二、实现 session
终端安装一些必要的东西(koa-generic-session、koa-redis、redis),更容易实现登录
npm i koa-generic-session koa-redis redis
修改 app.js 文件
app.js
const session = require('koa-generic-session')
const redisStore = require('koa-redis')
......
// session 配置(在routes前面)
app.keys = ['Qianduan2023']
app.use(session({// 配置 cookiercookie: {path: '/',httpOnly: true,maxAge: 24 * 60 * 60 * 1000},// 配置 redisstore: redisStore({all: '127.0.0.1:6379' // 本地 reids})
}))
我们在 user.js 中创建一个 session-test 做测试
user.js
router.get('/session-test', async function(ctx, next) {if (ctx.session.viewCount == null) {ctx.session.viewCount = 0}ctx.session.viewCount++ctx.body = {errno: 0,viewCount: ctx.session.viewCount}
})
三、开发路由
1、安装 mysql 和 xss
终端键入以下代码,安装 mysql 和 xss
npm i mysql xss
2、代码迁移
修改 app.js 文件,修改本地 redis 的写法
app.js
const { REDIS_CONF } = require('./conf/db')
......// 配置 redisstore: redisStore({// all: '127.0.0.1:6379' // 本地 reidsall: `${REDIS_CONF.host}:${REDIS_CONF.port}`})
3、修改 controller 控制器
修改 controller 文件里的内容(主要是修改成 async/await 的形式)
./controller.blog.js
// 导入执行 sql 的相关内容
const xss = require('xss')
const { exec } = require('../db/mysql')// 获取博客列表(通过作者和关键字)
const getList = async (author, keyword) => {// 1=1 是为了语法的绝对正确,注意以下 sql 拼接时的空格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;`// 返回 promisereturn await exec(sql)
}// 获取博客详情(通过 id)
const getDetail = async (id) => {const sql = `select * from blogs where id='${id}'`const rows = await exec(sql)return rows[0]
}// 新建博客 newBlog 若没有,就给它一个空对象
const newBlog = async (blogData = {}) => {// blogData 是一个博客对象,包含 title content author 属性const title = xss(blogData.title)const content = xss(blogData.content)const author = blogData.authorconst createTime = Date.now()const sql = `insert into blogs (title, content, createtime, author)values ('${title}', '${content}', '${createTime}', '${author}');`const insertData = await exec(sql)return {id: insertData.insertId}
}// 更新博客(通过 id 更新)
const updateBlog = async (id, blogData = {}) => {// id 就是要更新博客的 id// blogData 是一个博客对象 包含 title content 属性const title = xss(blogData.title)const content = xss(blogData.content)const sql = `update blogs set title='${title}', content='${content}' where id=${id}`const updateData = await exec(sql)// 更新的影响行数大于 0,则返回 trueif (updateData.affectedRows > 0) {return true}return false
}// 删除博客(通过 id 删除)
const delBlog = async (id, author) => {const sql = `delete from blogs where id='${id}' and author='${author}'`const delData = await exec(sql)if (delData.affectedRows > 0) {return true}return false
}// 导出共享
module.exports = {getList,getDetail,newBlog,updateBlog,delBlog
}
./controller/user.js
const { exec, escape } = require('../db/mysql')
const { genPassword } = require('../utils/cryp')
// 登录(通过用户名和密码)
const login = async (username, password) => {username = escape(username)// 生成加密密码password = genPassword(password)password = escape(password)const sql = `select username, realname from users where username=${username} and password=${password}`const rows = await exec(sql)return rows[0]}// 导出共享
module.exports = {login
}
4、开发路由
开发 ./routes 文件里的路由,直接拷贝 blog-express 文件里的内容,使用 koa2 规定的格式即可
./routes/blog.js
const router = require('koa-router')()
// 导入博客和用户控制器相关内容
const {getList,getDetail,newBlog,updateBlog,delBlog
} = require('../controller/blog')
// 导入成功和失败的模型
const {SuccessModel,ErrorModel
} = require('../model/resModel')const loginCheck = require('../middleware/loginCheck')
// 前缀
router.prefix('/api/blog')router.get('/list', async function (ctx, next) {// 博客的作者,req.query 用在 GET 请求中let author = ctx.query.author || ''// 博客的关键字const keyword = ctx.query.keyword || ''if (ctx.query.isadmin) {// 管理员界面if (ctx.session.username == null) {// 未登录ctx.body = new ErrorModel('未登录')return}// 强制查询自己的博客author = ctx.session.username}// 查询的结果const listData = await getList(author, keyword)ctx.body = new SuccessModel(listData)
})router.get('/detail', async function (ctx, next) {const data = await getDetail(ctx.query.id)ctx.body = new SuccessModel(data)
})router.post('/new', loginCheck, async function (ctx, next) {const body = ctx.request.bodybody.author = ctx.session.usernameconst data = await newBlog(body)ctx.body = new SuccessModel(data)
})router.post('/update', loginCheck, async function (ctx, next) {const val = await updateBlog(ctx.query.id, ctx.body)if (val) {ctx.body = new SuccessModel()} else {ctx.body = new ErrorModel('更新博客失败')}
})router.post('/del', loginCheck, async function (ctx, next) {const author = ctx.session.usernameconst val = await delBlog(ctx.query.id, author)if (val) {ctx.body = new SuccessModel()} else {ctx.body = new ErrorModel('删除博客失败')}
})module.exports = router
./routes/user.js
const router = require('koa-router')()
const { login } = require('../controller/user')
const { SuccessModel, ErrorModel } = require('../model/resModel')// 前缀
router.prefix('/api/user')// 路由的中间件必须是个 async 函数
router.post('/login', async function (ctx, next) {// 通过 request.body 获取const { username, password } = ctx.request.bodyconst data = await login(username, password)if (data.username) {// 设置 sessionctx.session.username = data.usernamectx.session.realname = data.realnamectx.body = new SuccessModel()return}ctx.body = new ErrorModel('登录失败')
})module.exports = router
四、联调
启动 redis,开启 nginx
后端:npm run dev
前端:http-server -p 8001
五、日志
终端键入安装 koa-morgan
npm i koa-morgan
修改 app.js 文件
app.js
const path = require('path')
const fs = require('fs')
const morgan = require('koa-morgan')
......
// 日志记录
const ENV = process.env.NODE_ENV
if (ENV !== 'production') {// 开发环境 / 测试环境app.use(morgan('dev'))
} else {// 线上环境使用 combined(写入文件)const logFileName = path.join(__dirname, 'logs', 'access.log')const writeStream = fs.createWriteStream(logFileName, {flags: 'a'})app.use(morgan('combined', {stream: writeStream}));
}
同样的,修改 package.json 文件
package.json
"prd": "cross-env NODE_ENV=production nodemon ./bin/www"
npm run prd,运行文件,之后打开几个页面,查看 access.log 文件的内容
六、写在最后
至此,我们明白了 如何使用 Koa2 框架对我们的 myblog 项目进行进一步的重构(实现session、开发路由、联调、日志), 本系列文章暂告一段落!
如果你需要该项目的 源码,请通过本篇文章最下面的方式 加入 进来~~
相关文章:
【Node.js实战】一文带你开发博客项目之Koa2重构(实现session、开发路由、联调、日志)
个人简介 👀个人主页: 前端杂货铺 🙋♂️学习方向: 主攻前端方向,也会涉及到服务端 📃个人状态: 在校大学生一枚,已拿多个前端 offer(秋招) 🚀未…...
第一部分:简单句——第二章:简单句的补充
简单句的核心构成:一主一谓 主语/宾语/表语 可以变成名词/代词/doing/to do 谓语动词有四种核心变化:三态 一否 时态语态情态否定 简单句的核心:将简单句给写对 简单句的补充:将简单句给写的更好、更充分 简单句的补充 1、限定…...
Spring Security简介
前面我们已经完成了传智健康后台管理系统的部分功能,例如检查项管理、检查组管理、套餐管理、预 约设置等。接下来我们需要思考2个问题: 问题1:在生产环境下我们如果不登录后台系统就可以完成这些功能操作吗? 答案显然是否定的&am…...
Hadoop安装 --- 简易安装Hadoop
目录 1、使用xftp工具 在opt目录下创建install和soft文件 2、使用xftp工具 将压缩包上传到install文件 3、编写shell脚本 3.1、创建目录来放shell脚本 3.2、创建autoinsatll.sh文件并修改权限 3.3、编写autoinsatll.sh 文件 刷新资源 运行文件 格式化 启动所有进程 Ha…...
俞军产品方法论,消化吸收,要点整理
一、总体概括二、产品经理、价值、用户模型、交易模型三、价值、产品和企业的价值生存游戏的常见要点:企业做产品的4方面产出:四、决策五、俞军产品方法论,认知迭代史1)俞军12条产品军规2)产品经理职级的背后影响因素:…...
spring注解的开端(@Component替代bean标签的使用)
目录 一、介绍 1.什么是注解开发? 2.Spring注解的版本 3.基于spring注解的应用 4. Component的细分注解 5.相关注解 二、简单例子讲解 1.类打注解 2.扫描注解放入工厂 3.总工厂取注解调用 4.运行结果 总结: 一、介绍 1.什么是注解开发&…...
Matlab傅里叶谱方法求解一维波动方程
傅里叶谱方法求解基本偏微分方程—一维波动方程 一维波动方程 对于一根两端固定、没有受到任何外力的弦, 若只研究其中的一段, 在不太长的时间 里, 固定端来不及对这段弦产生影响, 则可以认为固定端是不存在的, 弦的长度为无限大。 这种无界 (−∞<x<∞)(-\infty<x&…...
py3中 collections.Counter()函数典型例题
文章目录py3中 collections 的常用STL**Counter()** 函数**defaultdict()** 函数**deque()** 函数**orderedDict()** 函数(缺例题)小结py3中 collections 的常用STL 对于这个工具包非常好用,尤其是其中的 Counter() 函数 使用次数颇为频繁&a…...
Linux部署达梦数据库超详细教程
陈老老老板🦸👨💻本文专栏:国产数据库-达梦数据库👨💻本文简述:本文讲一下达梦数据库的下载与安装教程(Linux版),超级详细。👨💻…...
ctfshow 每周大挑战 极限命令执行
《简单的命令执行题目》 这里感叹一下,g4佬是真好厉害,这次题目十分的难,嗯,对我这种菜鸡来说是这样的,想了一天,最后结束了,也还是没有想明白第五题的解法,我真是fw,到最…...
使用vue3,vite,less,flask,python从零开始学习硅谷外卖(16-40集)
严正声明! 重要的事情说一遍,本文章仅供分享,文章和代码都是开源的,严禁以此牟利,严禁侵犯尚硅谷原作视频的任何权益,我知道学习编程的人各种各样的心思都有,但这不是你对开源社区侵权的理由&am…...
坚持就是胜利
很多朋友,可能坚持了多年的同等学力申硕考试,依然没有通过。如果你感到困惑,感到迷茫,要坚信:坚持就能胜利。有很多人跟你一样,一直坚持在路上,没有停止脚步。 生活没有你想象的那么好ÿ…...
代码中出现转置 pose (c2w,外参矩阵) 或者转置 intrinsic (内参)矩阵的原因
在代码中见到 pose(c2w),intrinsic 矩阵的转置,觉得比较奇怪。 后来想了一下为什么。下面解释一下: 用 c2w 矩阵举例子。理论上,一个 c2w 左乘上 一个相机坐标系下的点 P的坐标,能够得到该点在…...
2023 年腾讯云服务器配置价格表出炉(2核2G/2核4G/4核8G/8核16G、16核32G)
腾讯云轻量应用服务器为轻量级的云服务器,使用门槛低,按套餐形式购买,轻量应用服务器套餐自带的公网带宽较大,4M、6M、7M、10M、14M及20M套餐可选,如果是云服务器CVM这个带宽价格就要贵很多了。 1、轻量应用服务器优惠…...
相机出图画面一半清晰,一半模糊的原因是什么?
1、问题背景:在做项目的过程中,有遇到过几次,出图后画面是一半清晰,一半模糊的现象,再重新对焦也是一样。但换了个镜头后就好了,这应该是镜头的质量问题,但导致镜头出现这种问题的具体原因是什么…...
Rust学习入门--【4】Rust 输出到命令行
Rust 语言中的打印“函数” 学习新的编程语言时,大家都喜欢打印“Hello World”。 在Rust中怎样将字符串打印出来呢? Rust 输出文字的方式主要有两种:println!() 和 print!()。 “函数”差异说明: 这两个"函数"都是向…...
Vector刷写方案—vFlash工具介绍
我是穿拖鞋的汉子,魔都中坚持长期主义的工科男! 今天魔都天气是连阴雨,滴滴答答的下个不停,心情也跟着潮湿起来!老规矩分享一段喜欢的文字,避免成为高知识低文化的工程师: 即使在真正的困境里,也一直提示自己,每次自恋不得超过十分钟! 那些看似无法度过得困境,不是…...
【阶段总结】《非结构化信息分析应用与实践(筹)》
《非结构化信息分析应用与实践(筹)》Part 1.知识储备一、机器学习 1.几种常见的有监督学习算法 2.几种常见的无监督学习算法 3.数据挖掘基础知识 30 问 二、神经网络与深度学习 1.MP神经网络模型(附实例代码讲解) 2.图解LST…...
七大设计原则之迪米特法则应用
目录1 迪米特法则介绍2 迪米特法则应用1 迪米特法则介绍 迪米特原则(Law of Demeter LoD)是指一个对象应该对其他对象保持最少的了解,又叫最少知 道原则(Least Knowledge Principle,LKP),尽量降低类与类之…...
curl命令用法精简整理
目录1.GET请求1.1 形式1:1.2 形式2:2.POST请求2.1 无入参:2.2 form传参(文件):2.3 json入参:2.4 json文件入参:3.请求计时3.1 time命令(Linux):3.…...
Fluent Python 笔记 第 5 章 一等函数
在 Python 中,函数是一等对象。编程语言理论家把“一等对象”定义为满足下述条件的程 序实体: 在运行时创建能赋值给变量或数据结构中的元素 • 能作为参数传给函数能作为函数的返回结果 5.1 把函数视作对象 会用 map。 5.2 高阶函数 接受函数为参数࿰…...
卡尔曼滤波器与DSP实现
卡尔曼滤波器是利用系统状态方程,结合测量结果对系统状态进行进行最优估计的算法。本文介绍它的主要公式,并举例在C6000 DSP上实现。 推荐资料 KalmanFilter.NETUnderstanding Kalman Filters卡尔曼滤波与组合导航原理 “If you can’t explain it sim…...
引入QQ邮箱发送验证码进行安全校验
最近想给自己的项目在注册时加点安全校验,本想着使用短信验证码,奈何囊中羞涩只能退而求次改用QQ邮箱验证注册~ 一.需求分析 场景:用户输入自己的邮箱,点击获取验证码,后台会发送一封邮件到对应邮箱中。 分析&#x…...
【c++】数组
文章目录一维数组定义方式数组名案例案例1:元素逆置案例2:冒泡排序二维数组定义方式数组名案例:考试成绩统计数组特点: 1、每个数据元素放在一块连续的内存空间中; 2、数组中每个数据元素都是相同数据类型;…...
线程池的简单实现:Java线程池初学者必读指南
"作为一名Java开发者,是否曾经遇到过多线程并发的问题?线程数量过多时,会导致资源浪费,应用性能下降,甚至发生线程死锁的情况。那么,有没有一种方法可以有效地管理线程,避免这些问题呢&…...
【C#】[带格式的字符串] 复合格式设置字符串与使用 $ 的字符串内插 | 如何格式化输出字符串
复合格式输出 string name "Fred"; String.Format("Name {0}, hours {1:hh}", name, DateTime.Now);通过指定相同的参数说明符,多个格式项可以引用对象列表中的同一个元素。 例如,通过指定“0x{0:X} {0:E} {0:N}”等复合格式字符…...
Lecture4 反向传播(Back Propagation)
目录 1 问题背景 1.1计算图(Computational Graph) 1.2 激活函数(Activation Function)引入 1.3 问题引入 2 反向传播(Back Propagation) 2.1 为什么要使用反向传播 2.2 前馈运算(Forward Propagation…...
Power BI 筛选器函数---Window实例详解
一、Window函数 语法: Window ( <起始位置>,<起始位置类型>,<结束位置>,<结束位置类型>, [<关系>], [<OrderBy>],[空白],[PartitionBy] ) 含义: 对指定分区(PartitioinBy)中的行(关系表&…...
基础篇—如何创建css样式表,并集成到html文件中?
CSS 创建 HTML相当于一个页面的结构,CSS相当于页面的装饰,浏览器当读到一个样式表时,浏览器会根据它来格式化 HTML 文档。 如何插入样式表 插入样式表的方法有三种: 外部样式表(External style sheet)内部样式表(Internal style sheet)内联样式(Inline style)1、外…...
WindowsServer服务器系列:部署FTP文件服务
1、点击“开始”菜单,选择“服务器管理器” 2、在接下来弹出页面中选择“添加角色和功能” 3、接下来点击“下一步” 4、接下来选择“基于角色或基于功能的安装”并点击“下一步” 5、选择“从服务器池中选择服务器”并点击“下一步” 6、接下来选中“Web 服务器(II…...
寿光专业做网站的公司/域名注册服务网站哪个好
由于 WordPress大部分的主题都是使用Google的在线字体方案:Google Fonts. Google服务不稳定,导致大量独立博客字体加载不出来,直接导致博客打开速度变慢,严重时甚至导致网站打不开. 解决方案1: 编辑 WordPress 中的文…...
性咨询/百度推广seo优化
Safari浏览器: 1.safari执行history.go(-1);需要添加return false; Chrome浏览器 1.Chrome浏览器要预读图片,需要通过对图片的预加载。注:预加载前一定要将<img>加入<body> 1 /* 预加载图片 node-<img> func-回调函数 */ 2…...
物流业网站建设方案实验总结/品牌服务推广
非官方板卡应用非官方板卡也需要在官方提供的历程上进行修改,这样节省时间,而且AD936X的IP也需要参考官方的IP。下载并编译Xilinx u-boot源码选用的U-boot的网站为https://github.com/Xilinx/u-boot-xlnx直接命令git clone https://github.com/Xilinx/u-…...
伊春网站推广/一键优化
根据参考一些网络资料收集整理:因为使用代码添加的 每一次添加一个控件容器控件内部都做了自动计算 主要在这两句代码headerRow.Cells.Add(headerCell);detailRow.Cells.Add(detailCell);向XRTableRow 添加 XRTableCell 第一次新添加的会XRTableCell会以XRTableRow 的宽度值第二…...
郑州做网站的多不多/百度知道登录
目录 一.什么是火墙 二. iptables和filrewalld两种工具的切换 三.firewalld 1.火墙的域 2.设定原理及数据存储 3.firewalld 的管理命令 4.firewalld高级规则 三.iptables 1.五条链 2.三张表 3.iptables 4.数据包状态 5.dnat,snat 一.什么是火墙 Natfilter 是集成到lin…...
网站建设算入会计分录/seo 网站推广
《Docker技术入门与实践》 机械工业出版社 第十八章 Docker核心技术 Docker 归根到底是一种容器虚拟化技术。 本章介绍Docker的核心实现技术,包括架构、命名空间、控制组、联合文件系统、虚拟网络技术等话题。 早期版本Docker底层是基于成熟的Linux Container&a…...