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
);

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

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
}

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()

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.监听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
  }
})

2.调用监听

main.js

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

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()
}

登录接口

登录接口的实现同注册接口
登录有使用到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()

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)
  }
}

自动注册所有路由

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
const Koa = require('koa')
const registerAllRouters = require('../router')

const app = new Koa()

// 注册所有路由中间件
// 传入app
registerAllRouters(app)

module.exports = app

用户动态接口

创建数据库

-- 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)
);

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
);

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

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

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

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

标签接口(多对多)

创建数据库

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

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
);
-- 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)
);
-- 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.定义接口
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 ?

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

错误做法:返回的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
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

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

创建数据库

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

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

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

给user表添加头像字段

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值