个人简介
?个人主页: 前端杂货铺
 ?♂️学习方向: 主攻前端方向,也会涉及到服务端
 ?个人状态: 在校大学生一枚,已拿多个前端 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 和 xss2、代码迁移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({  // 配置 cookier  cookie: {    path: '/',    httpOnly: true,    maxAge: 24 * 60 * 60 * 1000  },  // 配置 redis  store: 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 xss2、代码迁移

修改 app.js 文件,修改本地 redis 的写法
app.js
const { REDIS_CONF } = require('./conf/db')......  // 配置 redis  store: redisStore({    // all: '127.0.0.1:6379' // 本地 reids    all: `${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;`    // 返回 promise    return 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.author    const 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,则返回 true    if (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.body    body.author = ctx.session.username    const 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.username    const 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.body    const data = await login(username, password)        if (data.username) {        // 设置 session        ctx.session.username = data.username        ctx.session.realname = data.realname        ctx.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_ENVif (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、开发路由、联调、日志), 本系列文章暂告一段落!
如果你需要该项目的 源码,请通过本篇文章最下面的方式 加入 进来~~
