• koa-apis


    注册接口

    创建数据库

    -- user表
    CREATE TABLE IF NOT EXISTS `user` (
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	name VARCHAR(30) UNIQUE NOT NULL,
    	password VARCHAR(50) NOT NULL,
    	createTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    	updateTime TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    router路由

    const KoaRouter = require('@koa/router')
    const userController = require('../controller/user.controller')
    const { verifyUser, handlePassword } = require('../middleware/user.middleware')
    
    // 1.定义路由对象
    const userRouter = new KoaRouter({ prefix: "/users" })
    
    // 2.具体路由规则
    // 2.1用户注册
    // verifyUser 验证注册合法性中间件
    // handlePassword 密码加密中间件
    // userController.create 插入数据库
    userRouter.post('/register', verifyUser, handlePassword, userController.create)
    
    module.exports = userRouter
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    middleware中间件

    一般为公用的中间件函数

    const userService = require("../service/user.service")
    const { NAME_OR_PWD_IS_REQUIRED, NAME_IS_ALREADY_EXISTS } = require("../config/error")
    const md5Encryption = require("../utils/md5-encryption")
    
    // 验证用户注册中间件
    const verifyUser = async (ctx, next) => {
      const { name, password } = ctx.request.body
    
      if (!name || !password) {
        return ctx.app.emit('error', NAME_OR_PWD_IS_REQUIRED, ctx)
      }
    
      const users = await userService.findUserByName(name)
      if (users.length > 0) {
        return ctx.app.emit('error', NAME_IS_ALREADY_EXISTS, ctx)
      }
      await next()
    }
    
    // 加密存储中间件
    const handlePassword = async (ctx, next) => {
      const { password } = ctx.request.body
      ctx.request.body.password = md5Encryption(password)
      await next()
    }
    
    module.exports = {
      verifyUser,
      handlePassword
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    controller控制器

    一般为具体路由对应的中间件函数
    调用service定义的数据库方法

    const userService = require("../service/user.service");
    
    class UserController {
      async create(ctx, next) {
        const user = ctx.request.body
        // 将请求参数传递给数据库操作中间件
        let res = await userService.create(user)
        ctx.body = {
          msg: '创建用户成功',
          data: res
        }
      }
    }
    
    module.exports = new UserController()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    service数据库操作

    数据库驱动mysql2操作数据库

    const connection = require('../app/database')
    
    class UserService {
      // 插入数据到数据库
      async create(user) {
    
        // 1.获取请求参数
        const { name, password } = user
    
        // 2.定义预处理语句
        const statement = 'INSERT INTO `user` ( `name`, `password`) VALUES (?, ?);'
    
        // 3.执行sql语句
        let [res, fields] = await connection.execute(statement, [name, password])
        return res
      }
    
      // 查找数据
      async findUserByName(name) {
        const statement = 'SELECT* FROM `user` WHERE `name` = ?;'
    
        let [res, fields] = await connection.execute(statement, [name])
        return res
      }
    }
    
    module.exports = new UserService()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    错误统一处理

    1.监听error事件

    handle-error.js

    const app = require("../app");
    const { NAME_OR_PWD_IS_REQUIRED, NAME_IS_ALREADY_EXISTS } = require("../config/error");
    
    app.on('error', (errType, ctx) => {
      let code = 0
      let msg = '未知错误'
    
      switch (errType) {
        case NAME_OR_PWD_IS_REQUIRED:
          code = -1001
          msg = '用户名或密码不能为空'
          break;
        case NAME_IS_ALREADY_EXISTS:
          code = -1002
          msg = '用户已经注册'
        default:
          break;
      }
    
      ctx.body = {
        code,
        msg
      }
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    2.调用监听

    main.js

    // require导入时,能执行该文件
    require('./utils/handle-error')
    
    • 1
    • 2

    3.抛出错误事件

    // 验证用户注册中间件
    const verifyUser = async (ctx, next) => {
      const { name, password } = ctx.request.body
    
      if (!name || !password) {
        return ctx.app.emit('error', NAME_OR_PWD_IS_REQUIRED, ctx)
      }
    
      const users = await userService.findUserByName(name)
      if (users.length > 0) {
        return ctx.app.emit('error', NAME_IS_ALREADY_EXISTS, ctx)
      }
      await next()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    登录接口

    登录接口的实现同注册接口
    登录有使用到token令牌的颁发与验证
    会话控制知识点

    token令牌的颁发

    const jwt = require('jsonwebtoken')
    const { PRIVATE_KEY } = require('../config/serect')
    
    class LoginController {
      
      async sign(ctx, next) {
        const { id, name } = ctx.user
    
        // 使用私钥PRIVATE_KEY颁发token令牌
        const payload = { id,name}
        const token = jwt.sign(payload, PRIVATE_KEY, {
          expiresIn: 60,
          algorithm: 'RS256'
        })
    
        ctx.body = {
          code:200,
          data:{ id,name,token }
        }
      }
    }
    
    module.exports = new LoginController()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    token令牌的验证

    验证token令牌中间件

    const verifyAuth = async (ctx, next) => {
      // 1.获取token
      const authorization = ctx.header.authorization
      const token = authorization.replace('Bearer ', '')
    
      // 2.使用公钥验证token
      try { 
        const res = jwt.verify(token, PUBLIC_KEY, {
          algorithms: ['RS256']
        })
        await next()
      } catch (error) {
        ctx.app.emit('error', UNAUTHORIZED, ctx)
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    自动注册所有路由

    const fs = require('fs')
    
    function registerAllRouters(app) {
    
      // 1.读取路由文件所在文件夹
      const files = fs.readdirSync(__dirname)
    
      // 2.读取路由文件
      for (const file of files) {
        if(!file.endsWith('router.js')) continue
        // 导入具体路由对象
        const router = require(`./${file}`)
        // 注册路由
        app.use(router.routes())
        app.use(router.allowedMethods())
      }
    }
    
    module.exports = registerAllRouters
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    const Koa = require('koa')
    const registerAllRouters = require('../router')
    
    const app = new Koa()
    
    // 注册所有路由中间件
    // 传入app
    registerAllRouters(app)
    
    module.exports = app
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    用户动态接口

    创建数据库

    -- moment(动态)表
    CREATE TABLE IF NOT EXISTS `moment` (
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	content VARCHAR(1000) NOT NULL,
    	user_id INT NOT NULL,
    	createTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    	updateTime TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    	FOREIGN KEY(user_id) REFERENCES `user`(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    user_id表示发表动态的用户,故user_id来自用户表,使用外键约束

    发表动态

    获取请求参数
    在这里插入图片描述
    在这里插入图片描述

    获取动态列表

    1.定义接口
    在这里插入图片描述
    2.中间件函数
    在这里插入图片描述
    3.操作数据库
    在这里插入图片描述

    删除、修改

    流程同上述接口的编写,删除需添加一个操作资源的权限验证。

    1.添加验证权限的中间件
    在这里插入图片描述

    2.编写中间件
    注:接口的params参数需严格同一命名规范 [tableName]Id
    在这里插入图片描述

    3.验证权限的实现
    在这里插入图片描述

    评论接口(一对多)

    创建数据库

    -- comment评论表
    -- 一对多:一条动态有多个评论
    CREATE TABLE IF NOT EXISTS `comment` (
    	-- 该条评论的id
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	-- 该条评论的内容
    	content VARCHAR(1000) NOT NULL,
    	-- 被评论动态的id
    	moment_id INT NOT NULL,
    	-- 发表这条评论的用户id
    	user_id INT NOT NULL,
    	-- 回复某条评论的评论id
    	comment_id INT NOT NULL,
    	createTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    	updateTime TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    	
    	FOREIGN KEY(moment_id) REFERENCES moment(id) ON DELETE CASCADE ON UPDATE CASCADE,
    	FOREIGN KEY(user_id) REFERENCES `user`(id) ON DELETE CASCADE ON UPDATE CASCADE,
    	FOREIGN KEY(comment_id) REFERENCES `comment`(id) ON DELETE CASCADE ON UPDATE CASCADE
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    给动态列表添加评论数字段

    SELECT 
    	m.id id, m.content content,m.createTime createTime,m.updateTime updateTime,
    	JSON_OBJECT('id',u.id,'name',u.`name`) `user`,
    	( SELECT COUNT(*) FROM `comment` WHERE `comment`.moment_id = m.id ) commentCounts
    FROM `moment` m
    LEFT JOIN `user` u ON m.user_id = u.id
    LIMIT 10 OFFSET 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    给动态详情添加评论列表字段

    SELECT 
    	m.id id, m.content content,m.createTime createTime,m.updateTime updateTime,
    	JSON_OBJECT('id',u.id,'name',u.name) user,
    	(
    		JSON_ARRAYAGG(JSON_OBJECT(
    			'id', c.id, 'content', c.content, 'commentId', c.comment_id,
    			'user', JSON_OBJECT('id', cu.id, 'name', cu.name)
    		))
    	) comments
    FROM moment m
    LEFT JOIN user u ON m.user_id = u.id
    LEFT JOIN `comment` c ON c.moment_id = m.id
    LEFT JOIN `user` cu ON cu.id = c.user_id
    WHERE m.id = 1
    GROUP BY m.id
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    标签接口(多对多)

    创建数据库

    一个标签对应多个动态,一个动态对应多个标签

    CREATE TABLE IF NOT EXISTS `lable` (
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	name VARCHAR(10) NOT NULL UNIQUE,
    	createTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    	updateTime TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    -- moment(动态)表
    CREATE TABLE IF NOT EXISTS `moment` (
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	content VARCHAR(1000) NOT NULL,
    	user_id INT NOT NULL,
    	createTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    	updateTime TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    	FOREIGN KEY(user_id) REFERENCES `user`(id)
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    -- moment和lable的关系表
    CREATE TABLE IF NOT EXISTS `moment_lable` (
    	moment_id INT NOT NULL,
    	lable_id INT NOT NULL,
    	createTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    	updateTime TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    	PRIMARY KEY(moment_id,lable_id),
    	FOREIGN KEY(moment_id) REFERENCES moment(id) ON DELETE CASCADE ON UPDATE CASCADE,
    	FOREIGN KEY(lable_id) REFERENCES lable(id) ON DELETE CASCADE ON UPDATE CASCADE
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    给动态添加标签

    1.定义接口
    verifyAuth 验证登录
    verifyPermission 验证有操作资源的权限
    verifyLableExists 将已存在和新创建的lable合并,添加到lable数据库中
    addLables 将moment_id与lable_id添加到关系表中
    在这里插入图片描述
    2.中间件
    在这里插入图片描述
    3.控制器
    在这里插入图片描述
    4数据库操作
    在这里插入图片描述

    给查询动态添加标签数字段

    SELECT 
      m.id id, m.content content,m.createTime createTime,m.updateTime updateTime,
      JSON_OBJECT('id',u.id,'name',u.name) user,
      ( SELECT COUNT(*) FROM comment WHERE comment.moment_id = m.id ) commentCounts,
      -- 添加的字段lableCounts
      ( SELECT COUNT(*) FROM moment_lable ml WHERE ml.moment_id = m.id ) lableCounts
    FROM moment m
    LEFT JOIN user u ON m.user_id = u.id
    LIMIT ? OFFSET ?
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    给查询动态详情添加标签列表字段

    错误做法:返回的lables数据个数是正确的2倍
    在这里插入图片描述

    正确做法:子查询

    -- 原先的sql查询
    SELECT 
    	m.id id, m.content content,m.createTime createTime,m.updateTime updateTime,
    	JSON_OBJECT('id',u.id,'name',u.name) user,
    	(
    		JSON_ARRAYAGG(JSON_OBJECT(
    			'id', c.id, 'content', c.content, 'commentId', c.comment_id,
    			'user', JSON_OBJECT('id', cu.id, 'name', cu.name)
    		))
    	) comments
    FROM moment m
    LEFT JOIN user u ON m.user_id = u.id
    LEFT JOIN `comment` c ON c.moment_id = m.id
    LEFT JOIN `user` cu ON cu.id = c.user_id
    WHERE m.id = 1
    GROUP BY m.id
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    SELECT 
    	m.id id, m.content content,m.createTime createTime,m.updateTime updateTime,
    	JSON_OBJECT('id',u.id,'name',u.name) user,
    	(
    		SELECT
    			JSON_ARRAYAGG(JSON_OBJECT(
    				'id', c.id, 'content', c.content, 'commentId', c.comment_id,
    				'user', JSON_OBJECT('id', cu.id, 'name', cu.name)
    			))
    		FROM `comment` c
    		LEFT JOIN `user` cu ON cu.id = c.user_id
    		WHERE c.moment_id = m.id
    	) comments,
    	(
    		JSON_ARRAYAGG(JSON_OBJECT(
    			'id', l.id, 'name', l.name
    		))
    	) lables
    FROM moment m
    LEFT JOIN user u ON m.user_id = u.id
    LEFT JOIN moment_lable ml ON ml.moment_id = m.id
    LEFT JOIN lable l ON l.id = ml.lable_id
    WHERE m.id = 1
    GROUP BY m.id
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    单文件上传接口(头像上传接口)

    创建数据库

    -- avatar 头像表
    CREATE TABLE IF NOT EXISTS `avatar` (
    	id INT PRIMARY KEY AUTO_INCREMENT,
    	filename VARCHAR(255) NOT NULL UNIQUE,
    	mimetype VARCHAR(255),
    	size INT,
    	user_id INT,
    	createTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    	updateTime TIMESTAMP DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
    	FOREIGN KEY (user_id) REFERENCES `user`(id) ON DELETE CASCADE ON UPDATE CASCADE
    );
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    相对路径问题

    在这里插入图片描述

    获取头像并显示接口

    1.通过该路径访问userId用户的头像
    在这里插入图片描述
    2.从数据库中读取文件信息,到存储磁盘中找到文件并显示
    在这里插入图片描述
    浏览器访问http://localhost:8000/users/avatar/9 可显示图片

    存储路径AVATAR_UPLOAD_PATH保存为常量,便于修改

    3.查询数据库具体操作
    在这里插入图片描述

    给user表添加头像字段

    用户表user更新avatar_url
    在这里插入图片描述

    其他查询中有用户信息时,可以携带上用户头像信息
    在这里插入图片描述

  • 相关阅读:
    知识图谱:推理【单跳查询(1-hop)、路径查询(2-hop、3-hop)、联合查询(conjunctive query)】
    星火绘镜Typemovie:释放创意,轻松制作你的短视频故事
    使用IE浏览器将pfx转为cer证书
    python绘制三维图
    Python中的元类(metaclass)
    可编程直流电源的特点都有哪些呢?
    前端---CSS的样式汇总
    6个国内外高质量icon素材网站分享给你,实用不花钱!
    【Linux】内存屏障
    Synopsys EDA Tools 安装问题记录
  • 原文地址:https://blog.csdn.net/qq_43551056/article/details/132723830