【Node.js实战】一文带你开发博客项目之Koa2重构(实现session、开发路由、联调、日志)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: 【Node.js实战】一文带你开发博客项目之Koa2重构(实现session、开发路由、联调、日志)

一、前言

前面我们介绍了 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
    }
})

999fbb1ca4454848afd2b9d2b8f026d1.png

三、开发路由

1、安装 mysql 和 xss

终端键入以下代码,安装 mysql 和 xss

npm i mysql xss

2、代码迁移

be5fc3383b0b44d0b40528f91619749e.png

修改 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

aa3a66073bac41edbe1db7c2b927bc04.png

34f3f283f6554d0f8b232cae6dbaa1a1.png

3c38e8562bd742d58e238ea884d39b87.png

五、日志

终端键入安装 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 文件的内容

bba4ee41dbac4d0f9318ec49fa9edbe0.png

六、写在最后

至此,我们明白了 如何使用 Koa2 框架对我们的 myblog 项目进行进一步的重构(实现session、开发路由、联调、日志), 本系列文章暂告一段落!

如果你需要该项目的 源码,请通过本篇文章最下面的方式 加入 进来~~

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
18天前
|
JSON 自然语言处理 前端开发
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
118 72
【01】对APP进行语言包功能开发-APP自动识别地区ip后分配对应的语言功能复杂吗?-成熟app项目语言包功能定制开发-前端以uniapp-基于vue.js后端以laravel基于php为例项目实战-优雅草卓伊凡
|
8天前
|
监控 JavaScript 前端开发
MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver:原理与实战案例
MutationObserver 是一个非常强大的 API,提供了一种高效、灵活的方式来监听和响应 DOM 变化。它解决了传统 DOM 事件监听器的诸多局限性,通过异步、批量的方式处理 DOM 变化,大大提高了性能和效率。在实际开发中,合理使用 MutationObserver 可以帮助我们更好地控制 DOM 操作,提高代码的健壮性和可维护性。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
MutationObserver详解+案例——深入理解 JavaScript 中的 MutationObserver:原理与实战案例
|
1月前
|
前端开发 JavaScript Java
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
144 13
【03】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架搭建-服务端-后台管理-整体搭建-优雅草卓伊凡商业项目实战
|
1月前
|
人工智能 JavaScript 关系型数据库
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
107 14
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
|
1月前
|
SQL JavaScript 安全
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
102 11
【04】Java+若依+vue.js技术栈实现钱包积分管理系统项目-若依框架二次开发准备工作-以及建立初步后端目录菜单列-优雅草卓伊凡商业项目实战
|
1月前
|
人工智能 JavaScript 安全
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
113 13
【01】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-需求改为思维导图-设计数据库-确定基础架构和设计-优雅草卓伊凡商业项目实战
|
1月前
|
存储 缓存 关系型数据库
图解MySQL【日志】——Redo Log
Redo Log(重做日志)是数据库中用于记录数据页修改的物理日志,确保事务的持久性和一致性。其主要作用包括崩溃恢复、提高性能和保证事务一致性。Redo Log 通过先写日志的方式,在内存中缓存修改操作,并在适当时候刷入磁盘,减少随机写入带来的性能损耗。WAL(Write-Ahead Logging)技术的核心思想是先将修改操作记录到日志文件中,再择机写入磁盘,从而实现高效且安全的数据持久化。Redo Log 的持久化过程涉及 Redo Log Buffer 和不同刷盘时机的控制参数(如 `innodb_flush_log_at_trx_commit`),以平衡性能与数据安全性。
33 5
图解MySQL【日志】——Redo Log
|
4月前
|
XML 安全 Java
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
本文介绍了Java日志框架的基本概念和使用方法,重点讨论了SLF4J、Log4j、Logback和Log4j2之间的关系及其性能对比。SLF4J作为一个日志抽象层,允许开发者使用统一的日志接口,而Log4j、Logback和Log4j2则是具体的日志实现框架。Log4j2在性能上优于Logback,推荐在新项目中使用。文章还详细说明了如何在Spring Boot项目中配置Log4j2和Logback,以及如何使用Lombok简化日志记录。最后,提供了一些日志配置的最佳实践,包括滚动日志、统一日志格式和提高日志性能的方法。
1315 31
【日志框架整合】Slf4j、Log4j、Log4j2、Logback配置模板
|
3月前
|
监控 安全 Apache
什么是Apache日志?为什么Apache日志分析很重要?
Apache是全球广泛使用的Web服务器软件,支持超过30%的活跃网站。它通过接收和处理HTTP请求,与后端服务器通信,返回响应并记录日志,确保网页请求的快速准确处理。Apache日志分为访问日志和错误日志,对提升用户体验、保障安全及优化性能至关重要。EventLog Analyzer等工具可有效管理和分析这些日志,增强Web服务的安全性和可靠性。
101 9
|
1月前
|
存储 SQL 关系型数据库
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log、原理、写入过程;binlog与redolog区别、update语句的执行流程、两阶段提交、主从复制、三种日志的使用场景;查询日志、慢查询日志、错误日志等其他几类日志
125 35
MySQL日志详解——日志分类、二进制日志bin log、回滚日志undo log、重做日志redo log

热门文章

最新文章