大事件项目实战

初始化

创建项目

新建api_server文件夹为项目根目录,并在项目中运行如下的命令,初始化管理配置文件:

npm init -y

运行如下的命令,安装特定版本的express:

npm i express@4.17.1

在项目根目录中新建app.js作为整个项目的入口文件,并初始化如下的代码:

javascript 复制代码
// 导入 express
const express = require('express')
// 创建服务器的实例对象
const app = express()
// 启动服务器
app.listen(3007, () => {
  console.log('api server running at http://127.0.0.1:3007')
})

配置cors跨域(解决接口的跨越问题)

运行如下的命令,安装cors中间件:

javascript 复制代码
npm i cors@2.8.5

在app.js中导入并配置cors中间件

javascript 复制代码
// 导入并配置 cors 中间件
const cors = require('cors')
// 将cors注册为全局中间件
app.use(cors())

配置解析表单数据的中间件

通过如下的代码,配置解析application/x-www-form-urlencoded格式的表单数据的中间件:

javascript 复制代码
// 配置解析表单数据的中间件,注意:这个中间件,只能解析 application/x-www-form-urlencoded 格式的表单数据
app.use(express.urlencoded({ extended: false }))

优化res.send()代码

在处理函数中,需要多次调用res.send()向客户端响应处理失败的结果,为了简化代码,可以受冻封装一个res.cc()函数

在app.js中,所有路由之前,声明一个全局中间件,为res对象挂载一个res.cc()函数

javascript 复制代码
// 一定要在路由之前,封装 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()
})

初始化路由相关的文件夹

在项目根目录中,新建router文件夹,用来存放所有的路由模块

路由模块中,只存放客户端的请求与处理函数之间的映射关系

在项目根目录中,新建router-handler文件夹,用来存放所有的路由处理函数模块

路由处理函数模块中,专门负责存放每个路由对应的处理函数

初始化用户路由模块

在router文件夹中,新建user.js文件,作为用户的路由模块,并初始化代码如下:

javascript 复制代码
const express=require('express')
//创建路由对象
const router=express.Router()

//注册新用户
router.post('/reguser',(req,res)=>{
    res.send('reguser OK')
})

//登录
router.post('/login',(req,res)=>{
    res.send('login OK')
})
//将路由对象共享出去
module.exports=router 

在app.js中,导入并使用用户路由模块

javascript 复制代码
//导入并注册用户路由模块
const userRouter=require('./router/user')
app.use('/api',userRouter)//访问这个模块的路由时,必须加上前缀/api

抽离用户路由模块中的处理函数

目的:为了保证路由模块的纯粹性,所有的路由处理函数,必须抽离到对应的路由处理函数模块中

在/router_handler/user.js中,使用exports对象,分别向外共享如下两个路由处理函数:

javascript 复制代码
//在这里定义和用户相关的路由处理函数,供/router/user.js模块进行调用

//注册用户的处理函数
exports.regUser=(req,res)=>{
    res.send('resguser OK')
}

//登录的处理函数
exports.login=(req,res)=>{
    res.send('login OK')
}

将/router/user.js中的代码修改为如下结构:

javascript 复制代码
const express=require('express')
const router=express.Router()

//导入用户路由处理函数模块
const userHandler=require('../router_handler/user')

//注册新用户
router.post('/reguser',userHandler.regUser)
//登录
router.post('/login',userHandler.login)

module.exports=router

登陆注册

新建ev_users表

在my_db_01数据库中,新建ev_users表如下:

安装并配置mysql模块

在API接口项目中,需要安装mysql这个第三方模

块,来连接和操作MySQL数据库

运行如下命令,安装mysql模块:

javascript 复制代码
npm i mysql@2.18.1

在项目根目录中创建/db/index.js文件,在此自定义模块中创建数据库的连接对象:

javascript 复制代码
const mysql = require('mysql')

const db = mysql.createPool({
  host: '127.0.0.1',
  user: 'root',
  password: 'admin123',
  database: 'my_db_01',
})

module.exports = db

注册

实现步骤

检测表单数据是否合法

判断用户名和密码是否为空

javascript 复制代码
  // 获取客户端提交到服务器的用户信息
  const userinfo = req.body
  // 对表单中的数据,进行合法性的校验
  // if (!userinfo.username || !userinfo.password) {
  //   return res.send({ status: 1, message: '用户名或密码不合法!' })
  // }

检测用户名是否被占用

导入数据库操作模块

javascript 复制代码
// 导入数据库操作模块
const db = require('../db/index')

定义SQL语句

javascript 复制代码
// 定义 SQL 语句,查询用户名是否被占用
  const sqlStr = 'select * from ev_users where username=?'

执行SQL语句并根据结果判断用户名是否被占用:

javascript 复制代码
  db.query(sqlStr, userinfo.username, (err, results) => {
    // 执行 SQL 语句失败
    if (err) {
      // return res.send({ status: 1, message: err.message })
      return res.cc(err)
    }
    // 判断用户名是否被占用
    if (results.length > 0) {
      // return res.send({ status: 1, message: '用户名被占用,请更换其他用户名!' })
      return res.cc('用户名被占用,请更换其他用户名!')
    }

对密码进行加密处理

为了保证密码的安全性,不建议在数据库以明文的形式保存用户密码,推荐对密码进行加密存储

在当前项目中,使用bcryptjs对用户密码进行加密,优点:

  • 加密之后的密码,无法被逆向破解

  • 同一明文密码多次加密,得到的加密结果各不相同,保证了安全性

运行如下命令,安装指定版本的bcryptjs:

javascript 复制代码
npm i bcryptjs@2.4.3

在/router_handler/user.js中,导入bcryptjs:

javascript 复制代码
const bcrypt=require('bcryptjs')

在注册用户的处理函数中,确认用户名可用之后,调用bcrypt.hashSync(明文密码,随机盐的长度)方法,对用户的密码进行加密处理:

javascript 复制代码
// 调用 bcrypt.hashSync() 对密码进行加密,返回值是加密之后的密码字符串
    userinfo.password = bcrypt.hashSync(userinfo.password, 10)

插入新用户

定义插入用户的SQL语句:

javascript 复制代码
const sql='insert into ev_users set ?'

调用db.query()执行SQL语句,插入新用户:

javascript 复制代码
    db.query(sql, { username: userinfo.username, password: userinfo.password }, (err, results) => {
      // 判断 SQL 语句是否执行成功
      // if (err) return res.send({ status: 1, message: err.message })
      if (err) return res.cc(err)
      // 判断影响行数是否为 1
      // if (results.affectedRows !== 1) return res.send({ status: 1, message: '注册用户失败,请稍后再试!' })
      if (results.affectedRows !== 1) return res.cc('注册用户失败,请稍后再试!')
      // 注册用户成功
      // res.send({ status: 0, message: '注册成功!' })
      res.cc('注册成功!', 0)
    })

优化表单数据验证

表单验证的原则:前端验证为辅,后端验证为主,后端永远不要相信前端提交过来的任何内容

在实际开发中,前后端都是需要对表单的数据进行合法性的验证,而且,后端作为数据合法性验证的最后一个关卡,在拦截非法数据方面,起到了至关重要的作用。

单纯使用if...else...的形式对数据合法性进行验证,效率低下,出错率高,维护性差,因此,推荐使用第三方数据验证模块 ,来降低出错率,提高验证的效率和可维护性,让后端程序员把更多的精力放在核心业务逻辑的处理上

安装@hapi.joi包,为表单中携带的每个数据项,定义验证规则:

javascript 复制代码
npm install @hapi/joi@17.1.0

安装@escook/express-joi中间件,来实现自动对表单数据进行验证的功能

javascript 复制代码
npm i @escook/express-joi

新建/schema/users.js用户信息验证规则模块,并初始化代码如下:

javascript 复制代码
// 导入定义验证规则的包
const joi = require('@hapi/joi')
/**
 * string()
 * alphanum()值只能是包含a-z A-Z 0-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 = {
  //表示需要对req.body中的数据进行验证
  body: {
    username,
    password,
  },
}

router/usr.js

javascript 复制代码
const express = require('express')
const router = express.Router()

// 导入用户路由处理函数对应的模块
const user_handler = require('../router_handler/user')

// 1. 导入验证数据的中间件
const expressJoi = require('@escook/express-joi')
// 2. 导入需要的验证规则对象
const { reg_login_schema } = require('../schema/user')

// 注册新用户
router.post('/reguser', expressJoi(reg_login_schema), user_handler.regUser)
// 登录
router.post('/login', user_handler.login)

module.exports = router

app.js

javascript 复制代码
// 导入 express
const express = require('express')
// 创建服务器的实例对象
const app = express()
const joi = require('@hapi/joi')

// 导入并配置 cors 中间件
const cors = require('cors')
// 将cors注册为全局中间件
app.use(cors())

// 配置解析表单数据的中间件,注意:这个中间件,只能解析 application/x-www-form-urlencoded 格式的表单数据
app.use(express.urlencoded({ extended: false }))

// 一定要在路由之前,封装 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()
})

// 导入并使用用户路由模块
const userRouter = require('./router/user')
app.use('/api', userRouter)

// 定义错误级别的中间件
app.use((err, req, res, next) => {
  // 验证失败导致的错误
  if (err instanceof joi.ValidationError) return res.cc(err)
  // 未知的错误
  res.cc(err)
})

// 启动服务器
app.listen(3007, () => {
  console.log('api server running at http://127.0.0.1:3007')
})

登录

实现步骤

检测表单数据是否合法

将/router/user.js中登录的路由代码修改如下:

javascript 复制代码
// 登录
router.post('/login', expressJoi(reg_login_schema), user_handler.login)

根据用户名查询用户的数据

接收表单数据

javascript 复制代码
const userinfo=req.body

定义SQL语句

javascript 复制代码
const sql='select * from ev_users where username=?'

执行SQL语句,查询用户的数据

判断用户输入的密码是否正确

核心实现思路:调用bcrypt.compareSync(用户名提交的密码,数据库中的密码)方法比较密码是否一致

返回值是布尔值(true一致,false不一致)

生成JWT的Token字符串

核心注意点:在生成Token字符串的时候,一定要剔除密码和头像的值

通过ES6的高级语法,快速剔除密码和头像的值:

javascript 复制代码
//剔除完毕之后,user中只保留了用户的id,username,nickname,email这四个属性的值
const user={...result[0],password:'',user_pic:''}

运行如下的命令,安装生成Token字符串的包:

javascript 复制代码
npm i jsonwebtoken@8.5.1

在/router_handler/user.js模块的头部区域,导入jsonwebtoken包:

javascript 复制代码
//用这个包来生成Token字符串
const jwt=require('josnwebtoken')

创建config.js文件,并向外共享加密和还原Token的jwtSecretKey字符串:

javascript 复制代码
// 这是一个全局的配置文件

module.exports = {
  // 加密和解密 Token 的秘钥
  jwtSecretKey: 'itheima No1. ^_^',
  // token 的有效期
  expiresIn: '10h',
}

将用户信息对象加密成Token字符串:

javascript 复制代码
// 导入全局的配置文件
const config = require('../config')
const user = { ...results[0], password: '', user_pic: '' }
// 对用户的信息进行加密,生成 Token 字符串
const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresIn })

将生成的Token字符串响应给客户端:

javascript 复制代码
    res.send({
      status: 0,
      message: '登录成功!',
      token:  'Bearer ' + tokenStr,
    })
javascript 复制代码
  // 执行 SQL 语句,根据用户名查询用户的信息
  db.query(sql, userinfo.username, (err, results) => {
    // 执行 SQL 语句失败
    if (err) return res.cc(err)
    // 执行 SQL 语句成功,但是获取到的数据条数不等于 1
    if (results.length !== 1) return res.cc('登录失败!')

    // TODO:判断密码是否正确
    const compareResult = bcrypt.compareSync(userinfo.password, results[0].password)
    if (!compareResult) return res.cc('登录失败!')

    // TODO:在服务器端生成 Token 的字符串
    const user = { ...results[0], password: '', user_pic: '' }
    // 对用户的信息进行加密,生成 Token 字符串
    const tokenStr = jwt.sign(user, config.jwtSecretKey, { expiresIn: config.expiresIn })
    // 调用 res.send() 将 Token 响应给客户端
    res.send({
      status: 0,
      message: '登录成功!',
      token:  'Bearer ' + tokenStr,
    })
  })

配置解析Token的中间件

运行如下的命令,安装解析Token的中间件:

javascript 复制代码
npm i express-jwt@5.3.3

在app.js中注册路由之前,配置解析Token的中间件:

javascript 复制代码
// 一定要在路由之前配置解析 Token 的中间件
const expressJWT = require('express-jwt')
const config = require('./config')

app.use(expressJWT({ secret: config.jwtSecretKey }).unless({ path: [/^\/api/] }))
// 导入并使用用户信息的路由模块
const userinfoRouter = require('./router/userinfo')
app.use('/my', userinfoRouter)

// 定义错误级别的中间件
app.use((err, req, res, next) => {
  // 验证失败导致的错误
  if (err instanceof joi.ValidationError) return res.cc(err)
  // 身份认证失败后的错误
  if (err.name === 'UnauthorizedError') return res.cc('身份认证失败!')
  // 未知的错误
  res.cc(err)
})

个人中心

获取用户的基本信息

实现步骤

初始化路由模块

创建/router/userinfo.js路由模块,并初始化如下的代码结构:

javascript 复制代码
//导入express
const express=require('express')
//创建路由对象
const router=express.Router()
//获取用户的基本信息
router.get('/userinfo',(req,res)=>{
    res.send('ok')
})
//向外共享路由对象
module.exports=router

在app.js中导入并使用个人中心的路由模块:

javascript 复制代码
//导入并使用用户信息路由模块
const userinfoRouter=require('./router/userinfo')
//注意:以/my开头的接口,都是有权限的接口,需要进行Token身份认证
app.use('/my',userinfoRouter)

初始化路由处理函数模块

创建/router_handler/userinfo.js路由处理函数模块,并初始化如下的代码结构:

javascript 复制代码
//获取用户基本信息的处理函数
exports.getUserInfo=(req,res)=>{
    res.send('ok')
}                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              

修改/router/userinfo.js 中的代码如下:

javascript 复制代码
const express=require('express')
const router=express.Router()

//导入用户信息的处理函数模块
const userInfo_handler=require('../router_handler/userinfo')

//获取用户的基本信息
router.get('/userinfo',userinfo_handler.getUserInfo)
module.exports=router 

获取用户的基本信息

在/router_handler/userinfo.js头部导入数据库操作模块:

javascript 复制代码
//导入数据库操作模块
const db=require('../db/index')

定义SQL语句:

javascript 复制代码
//根据用户的id,查询用户的基本信息
//注意:为了防止用户的密码泄露,需要排除password字段
const sql='select id,username,nickname,email,user_pic from ev_users where id=?'

调用db.query()执行SQL语句:

javascript 复制代码
  // 调用 db.query() 执行 SQL 语句
  //注意:req对象上的user属性,是Token解析成功,express-jwt中间件帮我们挂载上去的
  db.query(sql, req.user.id, (err, results) => {
    // 执行 SQL 语句失败
    if (err) return res.cc(err)
    // 执行 SQL 语句成功,但是查询的结果可能为空
    if (results.length !== 1) return res.cc('获取用户信息失败!')

    // 用户信息获取成功
    res.send({
      status: 0,
      message: '获取用户信息成功!',
      data: results[0],
    })
  })

更新用户的基本信息

实现步骤

定义路由和处理函数

在/router/userinfo.js模块中,新增更新用户基本信息的路由:

javascript 复制代码
//更新用户的基本信息
router.post('/userinfo',userinfo_handler.updateUserInfo)

在/router_handler/userinfo.js模块中,定义并向外共享更新用户基本信息的路由处理函数:

javascript 复制代码
//更新用户基本信息的处理函数
exports.updateUserInfo=(req,res)=>{
    res.send('ok')
}

验证表单数据

在/schema/user.js验证规则模块中,定义id,nickname,email的验证规则如下:

javascript 复制代码
// 定义 id, nickname, email 的验证规则
const id = joi.number().integer().min(1).required()
const nickname = joi.string().required()
const user_email = joi.string().email().required()

并使用exports向外共享如下的验证规则对象:

javascript 复制代码
// 验证规则对象 - 更新用户基本信息
exports.update_userinfo_schema = {
  // 需要对 req.body 里面的数据进行验证
  body: {
    id,
    nickname,
    email: user_email,
  },
}

在/router/userinfo.js模块中,导入验证数据合法性的中间件:

javascript 复制代码
//导入验证数据合法性的中间件
const expressJoi=require('@escook/express-joi')

在/router/userinfo.js模块中,导入需要的验证规则对象:

javascript 复制代码
//导入需要的验证规则对象
const {update_userinfo_schema}=require('../schema/user')

在/router/userinfo.js模块中,修改更新用户的基本信息的路由如下:

javascript 复制代码
//更新用户的基本信息
router.post('userinfo',expressJoi(update_userinfo_schema),userinfo_handler_updateUserInfo)

实现更新用户基本信息的功能

定义待执行的SQL语句:

javascript 复制代码
const sql='update ev_users set ? where id=?'

调用db.query()执行SQL语句并传参:

javascript 复制代码
  db.query(sql, [req.body, req.body.id], (err, results) => {
    // 执行 SQL 语句失败
    if (err) return res.cc(err)
    // 执行 SQL 语句成功,但是影响行数不等于 1
    if (results.affectedRows !== 1) return res.cc('更新用户的基本信息失败!')
    // 成功
    res.cc('更新用户信息成功!', 0)
  })

重置密码

定义路由和处理函数

在/router/userinfo.js模块中,新增重置密码的路由

javascript 复制代码
//重置密码的路由
router.post('.updatepwd',userinfo_handler.updatePassword)

在/router_handler/userinfo.js模块中,定义并向外共享重置密码的路由处理函数

javascript 复制代码
//重置密码的处理函数
exports.updatePassword=(req,res)=>{
    res.send('ok')
}

验证表单数据

在/schema/user.js模块中,使用exports向外共享如下的验证规则对象:

javascript 复制代码
// 验证规则对象 - 更新密码
exports.update_password_schema = {
  body: {
    // 使用password这个规则,验证req.body.oldPwd的值
    oldPwd: password,
    // 使用joi.not(joi.ref('oldPwd')).concat(password)规则,验证req.body.newPwd的值
    // 解读:
    // joi.ref('oldPwd')表示newPwd的值必须和oldPwd的值保持一致
    // joi.not(joi.ref('oldPwd'))表示newPwd的值不能等于oldPwd的值
    // .concat()用于合并joi.not(joi.ref('oldPwd'))和password这两条验证规则
    newPwd: joi.not(joi.ref('oldPwd')).concat(password),
  },
}

在/router/userinfo.js模块中,导入需要的验证规则对象:

javascript 复制代码
// 导入需要的验证规则对象
const { update_userinfo_schema, update_password_schema, update_avatar_schema } = require('../schema/user')

并在重置密码的路由中,使用update_pas sword_schema规则验证表单的数据,示例代码如下:

javascript 复制代码
// 更新密码的路由
router.post('/updatepwd', expressJoi(update_password_schema), userinfo_handler.updatePassword)

实现重置密码的功能

根据id查询用户是否存在

判断提交的旧密码是否正确

对新密码进行bcrypt加密之后,更新到数据库中

javascript 复制代码
// 更新用户密码的处理函数
exports.updatePassword = (req, res) => {
  // 根据 id 查询用户的信息
  const sql = `select * from ev_users where id=?`
  // 执行根据 id 查询用户的信息的 SQL 语句
  db.query(sql, req.user.id, (err, results) => {
    // 执行 SQL 语句失败
    if (err) return res.cc(err)
    // 判断结果是否存在
    if (results.length !== 1) return res.cc('用户不存在!')

    // 判断密码是否正确
    const compareResult = bcrypt.compareSync(req.body.oldPwd, results[0].password)
    if (!compareResult) return res.cc('旧密码错误!')

    // 定义更新密码的 SQL 语句
    const sql = `update ev_users set password=? where id=?`
    // 对新密码进行加密处理
    const newPwd = bcrypt.hashSync(req.body.newPwd, 10)
    // 调用 db.query() 执行 SQL 语句
    db.query(sql, [newPwd, req.user.id], (err, results) => {
      // 执行 SQL 语句失败
      if (err) return res.cc(err)
      // 判断影响的行数
      if (results.affectedRows !== 1) return res.cc('更新密码失败!')
      // 成功
      res.cc('更新密码成功', 0)
    })
  })
}

更换头像

定义路由和处理函数

在/router/userinfo.js模块中,新增更新用户头像的路由:

javascript 复制代码
//更新用户头像的路由
router.post('/update/avatar',userinfo_handler.updateAvatar)

在/route_handler/userinfo.js模块中,定义并向外共享更新用户头像的路由处理函数

验证表单数据

在/schema/user.js验证规则模块中,定义avatar的验证规则如下:

javascript 复制代码
// 定义验证 avatar 头像的验证规则
// dataUri()指的值如下格式的字符串数据
// data:image/png;base64,VE9PTFOWVNFQ1JFVFM=
const avatar = joi.string().dataUri().required()

并使用exports向外共享如下的验证规则对象:

javascript 复制代码
// 验证规则对象 - 更新头像
exports.update_avatar_schema = {
  body: {
    avatar
  }
}

在/router/userinfo.js模块中,导入需要的验证规则对象

javascript 复制代码
// 更换头像的路由
router.post('/update/avatar', expressJoi(update_avatar_schema), userinfo_handler.updateAvatar)

实现更新用户头像的功能

定义更新用户头像的SQL语句

调用db.query()执行SQL语句,更新对应用户的头像

javascript 复制代码
// 更新用户头像的处理函数
exports.updateAvatar = (req, res) => {
  // 1. 定义更新头像的 SQL 语句
  const sql = `update ev_users set user_pic=? where id=?`
  // 2. 调用 db.query() 执行 SQL 语句
  db.query(sql, [req.body.avatar, req.user.id], (err, results) => {
    // 执行 SQL 语句失败
    if (err) return res.cc(err)
    // 影响的行数是否等于 1
    if (results.affectedRows !== 1) return res.cc('更换头像失败!')
    // 成功
    res.cc('更换头像成功!', 0)
  })
}

文章分类管理

新建 ev_article_cate 表

创建表结构

新增两条初始数据

获取文章分类列表

实现步骤

1. 初始化路由模块
  1. 创建 `/router/artcate.js` 路由模块,并初始化如下的代码结构:
javascript 复制代码
// 导入 express
const express = require('express')
// 创建路由对象
const router = express.Router()
// 获取文章分类的列表数据
router.get('/cates', (req, res) => {
  res.send('ok')
})
// 向外共享路由对象
module.exports = router
  1. 在 `app.js` 中导入并使用文章分类的路由模块:
javascript 复制代码
// 导入并使用文章分类路由模块
const artCateRouter = require('./router/artcate')
// 为文章分类的路由挂载统一的访问前缀 /my/article
app.use('/my/article', artCateRouter)
2. 初始化路由处理函数模块
  1. 创建 `/router_handler/artcate.js` 路由处理函数模块,并初始化如下的代码结构:
javascript 复制代码
// 获取文章分类列表数据的处理函数
exports.getArticleCates = (req, res) => {
  res.send('ok')
}
  1. 修改 `/router/artcate.js` 中的代码如下:
javascript 复制代码
const express = require('express')
const router = express.Router()
// 导入文章分类的路由处理函数模块
const artcate_handler = require('../router_handler/artcate')
// 获取文章分类的列表数据
router.get('/cates', artcate_handler.getArticleCates)
module.exports = router
3. 获取文章分类列表数据
  1. 在 `/router_handler/artcate.js` 头部导入数据库操作模块:
javascript 复制代码
// 导入数据库操作模块
const db = require('../db/index')
  1. 定义 SQL 语句:
javascript 复制代码
// 根据分类的状态,获取所有未被删除的分类列表数据
// is_delete 为 0 表示没有被 标记为删除 的数据
const sql = 'select * from ev_article_cate where is_delete=0 order by id asc'
  1. 调用 `db.query()` 执行 SQL 语句:
javascript 复制代码
db.query(sql, (err, results) => {
  // 1. 执行 SQL 语句失败
  if (err) return res.cc(err)
  // 2. 执行 SQL 语句成功
  res.send({
    status: 0,
    message: '获取文章分类列表成功!',
    data: results,
  })
})

新增文章分类

实现步骤

  1. 定义路由和处理函数

  2. 验证表单数据

  3. 查询 `分类名称` 与 `分类别名` 是否被占用

  4. 实现新增文章分类的功能

定义路由和处理函数

  1. 在 `/router/artcate.js` 模块中,添加 `新增文章分类` 的路由:
javascript 复制代码
// 新增文章分类的路由
router.post('/addcates', artcate_handler.addArticleCates)
  1. 在 `/router_handler/artcate.js` 模块中,定义并向外共享 `新增文章分类` 的路由处理函数:
javascript 复制代码
// 新增文章分类的处理函数
exports.addArticleCates = (req, res) => {
  res.send('ok')
}

验证表单数据

  1. 创建 `/schema/artcate.js` 文章分类数据验证模块,并定义如下的验证规则:
javascript 复制代码
// 导入定义验证规则的模块
const joi = require('@hapi/joi')
// 定义 分类名称 和 分类别名 的校验规则
const name = joi.string().required()
const alias = joi.string().alphanum().required()
// 校验规则对象 - 添加分类
exports.add_cate_schema = {
  body: {
    name,
    alias,
  },
}
  1. 在 `/router/artcate.js` 模块中,使用 `add_cate_schema` 对数据进行验证:
javascript 复制代码
// 导入验证数据的中间件
const expressJoi = require('@escook/express-joi')
// 导入文章分类的验证模块
const { add_cate_schema } = require('../schema/artcate')
// 新增文章分类的路由
router.post('/addcates', expressJoi(add_cate_schema), artcate_handler.addArticleCates)

查询分类名称与别名是否被占用

  1. 定义查重的 SQL 语句:
javascript 复制代码
// 定义查询 分类名称 与 分类别名 是否被占用的 SQL 语句
const sql = `select * from ev_article_cate where name=? or alias=?`
  1. 调用 `db.query()` 执行查重的操作:
javascript 复制代码
// 执行查重操作
db.query(sql, [req.body.name, req.body.alias], (err, results) => {
  // 执行 SQL 语句失败
  if (err) return res.cc(err)
  // 判断 分类名称 和 分类别名 是否被占用
  if (results.length === 2) return res.cc('分类名称与别名被占用,请更换后重试!')
  // 分别判断 分类名称 和 分类别名 是否被占用
  if (results.length === 1 && results[0].name === req.body.name) return res.cc('分类名称被占用,请更换后重试!')
  if (results.length === 1 && results[0].alias === req.body.alias) return res.cc('分类别名被占用,请更换后重试!')
  // TODO:新增文章分类
})

实现新增文章分类的功能

  1. 定义新增文章分类的 SQL 语句:
javascript 复制代码
const sql = `insert into ev_article_cate set ?`
  1. 调用 `db.query()` 执行新增文章分类的 SQL 语句:
javascript 复制代码
db.query(sql, req.body, (err, results) => {
  // SQL 语句执行失败
  if (err) return res.cc(err)
  // SQL 语句执行成功,但是影响行数不等于 1
  if (results.affectedRows !== 1) return res.cc('新增文章分类失败!')
  // 新增文章分类成功
  res.cc('新增文章分类成功!', 0)
})

根据 Id 删除文章分类

实现步骤

  1. 定义路由和处理函数

  2. 验证表单数据

  3. 实现删除文章分类的功能

定义路由和处理函数

  1. 在 `/router/artcate.js` 模块中,添加 `删除文章分类` 的路由:
javascript 复制代码
// 删除文章分类的路由
router.get('/deletecate/:id', artcate_handler.deleteCateById)
  1. 在 `/router_handler/artcate.js` 模块中,定义并向外共享 `删除文章分类` 的路由处理函数:
javascript 复制代码
// 删除文章分类的处理函数
exports.deleteCateById = (req, res) => {
  res.send('ok')
}

验证表单数据

  1. 在 `/schema/artcate.js` 验证规则模块中,定义 id 的验证规则如下:
javascript 复制代码
// 定义 分类Id 的校验规则
const id = joi.number().integer().min(1).required()
  1. 并使用 `exports` 向外共享如下的 `验证规则对象`:
javascript 复制代码
// 校验规则对象 - 删除分类
exports.delete_cate_schema = {
  params: {
    id,
  },
}
  1. 在 `/router/artcate.js` 模块中,导入需要的验证规则对象,并在路由中使用:
javascript 复制代码
// 导入删除分类的验证规则对象
const { delete_cate_schema } = require('../schema/artcate')
// 删除文章分类的路由
router.get('/deletecate/:id', expressJoi(delete_cate_schema), artcate_handler.deleteCateById)

实现删除文章分类的功能

  1. 定义删除文章分类的 SQL 语句:
javascript 复制代码
const sql = `update ev_article_cate set is_delete=1 where id=?`
  1. 调用 `db.query()` 执行删除文章分类的 SQL 语句:
javascript 复制代码
db.query(sql, req.params.id, (err, results) => {
  // 执行 SQL 语句失败
  if (err) return res.cc(err)
  // SQL 语句执行成功,但是影响行数不等于 1
  if (results.affectedRows !== 1) return res.cc('删除文章分类失败!')
  // 删除文章分类成功
  res.cc('删除文章分类成功!', 0)
})

根据 Id 获取文章分类数据

实现步骤

  1. 定义路由和处理函数

  2. 验证表单数据

  3. 实现获取文章分类的功能

定义路由和处理函数

  1. 在 `/router/artcate.js` 模块中,添加 `根据 Id 获取文章分类` 的路由:
javascript 复制代码
router.get('/cates/:id', artcate_handler.getArticleById)
  1. 在 `/router_handler/artcate.js` 模块中,定义并向外共享 `根据 Id 获取文章分类` 的路由处理函数:
javascript 复制代码
// 根据 Id 获取文章分类的处理函数
exports.getArticleById = (req, res) => {
  res.send('ok')
}

验证表单数据

  1. 在 `/schema/artcate.js` 验证规则模块中,使用 `exports` 向外共享如下的 `验证规则对象`:
javascript 复制代码
// 校验规则对象 - 根据 Id 获取分类
exports.get_cate_schema = {
  params: {
    id,
  },
}
  1. 在 `/router/artcate.js` 模块中,导入需要的验证规则对象,并在路由中使用:
javascript 复制代码
// 导入根据 Id 获取分类的验证规则对象
const { get_cate_schema } = require('../schema/artcate')
// 根据 Id 获取文章分类的路由
router.get('/cates/:id', expressJoi(get_cate_schema), artcate_handler.getArticleById)

实现获取文章分类的功能

  1. 定义根据 Id 获取文章分类的 SQL 语句:
javascript 复制代码
const sql = `select * from ev_article_cate where id=?`
  1. 调用 `db.query()` 执行 SQL 语句:
javascript 复制代码
db.query(sql, req.params.id, (err, results) => {
  // 执行 SQL 语句失败
  if (err) return res.cc(err)
  // SQL 语句执行成功,但是没有查询到任何数据
  if (results.length !== 1) return res.cc('获取文章分类数据失败!')
  // 把数据响应给客户端
  res.send({
    status: 0,
    message: '获取文章分类数据成功!'
    data: results[0],
  })
})

根据 Id 更新文章分类数据

实现步骤

  1. 定义路由和处理函数

  2. 验证表单数据

  3. 查询 `分类名称` 与 `分类别名` 是否被占用

  4. 实现更新文章分类的功能

定义路由和处理函数

  1. 在 `/router/artcate.js` 模块中,添加 `更新文章分类` 的路由:
javascript 复制代码
// 更新文章分类的路由
router.post('/updatecate', artcate_handler.updateCateById)
  1. 在 `/router_handler/artcate.js` 模块中,定义并向外共享 `更新文章分类` 的路由处理函数:
javascript 复制代码
// 更新文章分类的处理函数
exports.updateCateById = (req, res) => {
  res.send('ok')
}

验证表单数据

  1. 在 `/schema/artcate.js` 验证规则模块中,使用 `exports` 向外共享如下的 `验证规则对象`:
javascript 复制代码
// 校验规则对象 - 更新分类
exports.update_cate_schema = {
  body: {
    Id: id,
    name,
    alias,
  },
}
  1. 在 `/router/artcate.js` 模块中,导入需要的验证规则对象,并在路由中使用:
javascript 复制代码
// 导入更新文章分类的验证规则对象
const { update_cate_schema } = require('../schema/artcate')
// 更新文章分类的路由
router.post('/updatecate', expressJoi(update_cate_schema), artcate_handler.updateCateById)

查询分类名称与别名是否被占用

  1. 定义查重的 SQL 语句:
javascript 复制代码
// 定义查询 分类名称 与 分类别名 是否被占用的 SQL 语句
const sql = `select * from ev_article_cate where Id<>? and (name=? or alias=?)`
  1. 调用 `db.query()` 执行查重的操作:
javascript 复制代码
// 执行查重操作
db.query(sql, [req.body.Id, req.body.name, req.body.alias], (err, results) => {
  // 执行 SQL 语句失败
  if (err) return res.cc(err)
  // 判断 分类名称 和 分类别名 是否被占用
  if (results.length === 2) return res.cc('分类名称与别名被占用,请更换后重试!')
  if (results.length === 1 && results[0].name === req.body.name) return res.cc('分类名称被占用,请更换后重试!')
  if (results.length === 1 && results[0].alias === req.body.alias) return res.cc('分类别名被占用,请更换后重试!')
  // TODO:更新文章分类
})

实现更新文章分类的功能

  1. 定义更新文章分类的 SQL 语句:
javascript 复制代码
const sql = `update ev_article_cate set ? where Id=?`
  1. 调用 `db.query()` 执行 SQL 语句:
javascript 复制代码
db.query(sql, [req.body, req.body.Id], (err, results) => {
  // 执行 SQL 语句失败
  if (err) return res.cc(err)
  // SQL 语句执行成功,但是影响行数不等于 1
  if (results.affectedRows !== 1) return res.cc('更新文章分类失败!')
  // 更新文章分类成功
  res.cc('更新文章分类成功!', 0)
})
相关推荐
OpenTiny社区1 小时前
Node.js 技术原理分析系列 4—— 使用 Chrome DevTools 分析 Node.js 性能问题
前端·开源·node.js·opentiny
huangfuyk1 小时前
使用Node.js从零搭建DeepSeek本地部署(Express框架、Ollama)
node.js·express·ollama·deepseek
FG.2 小时前
React
前端·react.js·前端框架
青红光硫化黑3 小时前
React基础之ReactRouter
前端·react.js·前端框架
Thomas游戏开发5 小时前
Unity3D网格简化与LOD技术详解
前端框架·unity3d·游戏开发
火龙谷5 小时前
hadoop第3课(hdfs shell常用命令)
hadoop·hdfs·npm
比特鹰5 小时前
桌面端跨端框架调研
前端·javascript·前端框架
华洛5 小时前
老板要求接入DeepSeek,哪家提供的服务强?
前端·javascript·node.js
菜鸟码农_Shi5 小时前
《NestJS vs Express:哪个更适合现代 Web 开发?》
node.js
Java陈序员5 小时前
IDEA 必备插件!轻松搞定 JSON 格式化!
java·json·intellij idea