Koa学习3:用户添加、错误处理
模型
在src目录下创建model目录,用来存放模型
创建用户模型
user.model.js
注意: UUID类型是无法自增的,将id设置为UUID类型时只需要为其指定默认值即可
// 数据类型
const { DataTypes } = require('sequelize');
// 导入已经连接了数据库的Sequlize对象
const sequelize = require('../db/seq');// 创建模型
const User = sequelize.define('zd_user', // 对应数据库里的表,默认会变成复数形式也就是 zd_users// 定义模型的属性(表的字段){// id会被自动创建,也可以不定义id: {type: DataTypes.UUID, // UUID 类型unique: true, // 是否唯一primaryKey: true, // 是否是主键comment: '主键', // 注释defaultValue:DataTypes.UUIDV4 // 设置uuid的生成规则},// 用户名user_name: {type: DataTypes.STRING, // 对应VARCHAR(255)unique: true,allowNull: false,comment: '用户名',// 验证器,用于校验格式,具体见官方文档validate: {isAlphanumeric: true, // 仅允许字符和数字len: [6, 10], //长度},},// 密码password: {type: DataTypes.CHAR(64),allowNull: false,comment: '密码',validate: {isAlphanumeric: true, // 仅允许字符和数字len: [6, 10], //长度},},// 是否是vipis_vip: {type: DataTypes.BOOLEAN,allowNull: false,defaultValue: 0, // 默认值,BOOLEAN类型的值只有0和1,填写true和false也会被自动转换comment: '是否是vip',},}
);// 模型同步,将创建表,如果表已经存在,则将其首先删除
// User.sync({ force: true });module.exports = User;
在项目根目录下运行该js文件,创建表(要在项目根目录下,不然无法读取到.env中的变量)

默认情况下,Sequelize 使用数据类型 DataTypes.DATE 自动向每个模型添加 createdAt 和 updatedAt 字段. 这些字段会自动进行管理 - 每当你使用Sequelize 创建或更新内容时,这些字段都会被自动设置. createdAt 字段将包含代表创建时刻的时间戳,而 updatedAt 字段将包含最新更新的时间戳.
注意: 这是在 Sequelize 级别完成的(即未使用 SQL触发器 完成). 这意味着直接 SQL 查询(例如,通过任何其他方式在不使用 Sequelize 的情况下执行的查询)将不会导致这些字段自动更新.
如果不想要下面这两个字段可以设置timestamps为false
sequelize.define('User', {// ... (属性)
}, {timestamps: false
});
添加用户
user.service
// 导入用户模型
const User = require('../model/user.model');
class UserService {// 创建用户async createUser(user_name, password) {// 插入数据,新增成功后会返回该条数据的对象const res = await User.create({ user_name, password });return res;}// 获取用户基本信息async getUserInfo(params = { id, user_name, is_vip }) {// 处理where条件const whereOpt = {};for (let key in params) {if (![undefined, null, ''].includes(params[key])) {whereOpt[key] = params[key];}}// 查询符合条件的第一条数据const res = await User.findOne({// 指定要返回的属性attributes: ['id', 'user_name', 'is_vip', 'createdAt', 'updatedAt'],// where条件where: whereOpt,});return res ? res : null;}
}// 导出
module.exports = new UserService();
user.controller
/*** 处理与用户有关的请求*///导入service
const { createUser, getUserInfo } = require('../service/user.service');class UserController {//注册async register(ctx, next) {// 1、获取数据const requistBody = ctx.request.body;// 合法性判断if (!requistBody.user_name || !requistBody.password) {console.error('用户名或密码为空');ctx.body = {code: '10001', // 错误code,用于定义错误类型,团队内部自行约定message: '用户名或密码为空',data: null,};return;}// 合理性,判断该用户是否已经存在,用户名是唯一的if (await getUserInfo({ user_name:requistBody.user_name })) {ctx.body = {code: '10002',message: '该用户已存在,请勿重新注册',data: null,};return;}// 2、操作数据库const res = await createUser(requistBody.user_name, requistBody.password);// 3、返回响应结果ctx.body = {code: 0,message: '用户创建成功',data: null,};}// 登录async login(ctx, next) {ctx.body = '登录';}
}// 导出实例化对象
module.exports = new UserController();


需要注意,当输入文本之类的信息时要避免字符串中的特殊字符,需要将其转义为HTML实体,避免恶意脚本注入。
const str = "<script>alert('hello world');</script>";
const escapedStr = sequelize.escape(str);
console.log(escapedStr); // 输出:"<script>alert('hello world');</script>"
错误处理
从上面的代码可以看到对于错误处理并不完善可能会出现一些其他的错误,并且如果每一个接口都要这样写的话是很难维护的,因此需要一个中间件进行统一的错误处理。
在src下创建一个constant用来定义一些常量,创建一个middleware文件夹用来存放错误处理的逻辑
错误类型
constant/user.err.type.js 用于记录与用户有关的错误类型
module.exports={userFormatError:{code: '10001', // 错误code,用于定义错误类型,团队内部自行约定message: '用户名或密码为空',data: null,},userAlreadyExisted:{code: '10002',message: '该用户已存在,请勿重新注册',data: null,},userRegisterErr:{code: '10003',message: '用户注册失败',data: null,}
}
constant/err.type.js 错误类型的统一出口,后续会加一些其他的错误类型
// 用户错误
const UserErr = require('./user.err.type');module.exports = {UserErr,
};
中间件
middleware/user.middleware.js
// 导入service层
const { getUserInfo } = require('../service/user.service');
// 导入错误类型
const { UserErr } = require('../constant/err.type');// 用户注册校验
const userRegisterValidator = async (ctx, next) => {// 获取入参const requistBody = ctx.request.body;// 合法性判断if (!requistBody.user_name || !requistBody.password) {// console.error 打印的内容会被记录到服务器的日志里console.error('用户名或密码为空:', requistBody);//使用了Koa的错误处理机制ctx.app.emit('error', UserErr.userFormatError, ctx);return;}// 合理性,判断该用户是否已经存在,用户名是唯一的if (await getUserInfo({ user_name: requistBody.user_name })) {console.error('该用户已存在,请勿重新注册:', requistBody);ctx.app.emit('error', UserErr.userAlreadyExisted, ctx);return;}// 验证通过后交由一个中间件处理await next();
};module.exports = {userRegisterValidator,
};
注意:
- console.error 打印的内容会被记录到服务器的日志里;
ctx.app.emit('error', UserErr.userAlreadyExisted, ctx),使用koa提供的错误机制让 Koa 自动捕获错误并返回给客户端
使用
router/user.route.js
// 注册
router.post('/register', userRegisterValidator, register);
先执行userRegisterValidator中间件进行注册校验,校验通过后在执行register中间件进行注册
app/index.js
const app = new Koa();
// koa-body中间件要在所有的路由之前
app.use(koaBody());// 中间件
app.use(userRouter.routes());// 进行统一的错误处理
app.on('error', (errType, ctx) => {ctx.body = errType;
});module.exports = app;
相关文章:
Koa学习3:用户添加、错误处理
模型 在src目录下创建model目录,用来存放模型 创建用户模型 user.model.js 注意: UUID类型是无法自增的,将id设置为UUID类型时只需要为其指定默认值即可 // 数据类型 const { DataTypes } require(sequelize); // 导入已经连接了数据库…...
网络安全入门学习第十五课——PHP基础
文章目录 一、WEB技术1、什么是web2、B/S架构3、C/S架构 二、PHP概述1、PHP是什么2、PHP受欢迎的原因3、基于MVC模式的PHP框架4、常用编译工具5、PHP环境搭建6、开发工具 三、PHP基本语法格式1、标记2、输出语句3、注释4、标识符 四、数据与运算1、常量1.1、常量定义1.2、预定义…...
电子科技大学 数学专业-功不唐捐,玉汝于成
电子科技大学 数学专业 功不唐捐,玉汝于成 1.本科背景 本科是坐落于湖南湘潭的湖南科技大学,专业为网络工程专业,因热爱数学专业,所以决定跨考数学专业。 本科专业课平均成绩85,排名10/104。CET 4 474分,…...
Android10.0 iptables用IOemNetd实现删除子链功能的实现
1.前言 在10.0的系统rom定制化开发中,在system中netd网络这块的产品需要中,会要求设置屏蔽ip地址之内的功能, liunx中iptables命令也是比较重要的,接下来就来在IOemNetd这块实现删除创建子链的相关功能 2. iptables用IOemNetd实现删除创建子链功能的实现的核心类 syste…...
OpenGL光照之光照贴图
文章目录 漫反射贴图镜面光贴图放射光贴图代码 每个物体都拥有自己独特的材质从而对光照做出不同的反应的方法。这样子能够很容易在一个光照的场景中给每个物体一个独特的外观,但是这仍不能对一个物体的视觉输出提供足够多的灵活性。 我们将整个物体的材质定义为一个…...
2018~2019 学年第二学期《信息安全》考试试题(B 卷)
北京信息科技大学 2018 ~2019 学年第 2 学期 《信息安全》课程期末考试试卷 B 课程所在学院:计算机学院 适用专业班级:计科 1601-06,重修 考试形式:(闭卷) 一. 选择题(本题满分 10 分,共含 10 道小题,每小题 1 分) 网络中存在的安全漏洞主…...
LeetCode-C#-0002.两数相加
0.声明 该题目来源于LeetCode 如有侵权,立马删除。 解法不唯一,如有新解法可一同讨论。 1.题目 0002两数相加 给你两个非空的链表,表示两个非负的整数,它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一…...
访问修饰符private,default,protected,public访问等级区别
private:private是最严格的访问修饰符,它将成员声明为私有的。私有成员只能在声明它们的类内部访问,其他类无法直接访问私有成员。这样可以确保数据的封装性和安全性。 default(默认):如果没有明确指定访问…...
阿里云(Linux)安装Docker教程
首先安装docker,需要找到帮助文档,那肯定是我们的官网: Install Docker Engine on CentOS | Docker Documentation 找到对应的位置,这里是安装在CentOS中,版本需要Ce…...
Linux C编程基础:获取时间
1.前言 对于linux下的编程,无论是用户态还是内核态,时间获取都是经常需要使用到的。以下分别从用户态和内核态整理了几个常用的时间获取接口,供编写代码时快速查阅。 2.用户态获取时间 2.1 clock_gettime() #include <time.h>int c…...
Spring核心注解
1、Bean注解 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中位置: 一般出现在方法上面属性: name:用于指定bean的id。当不写时,默认值是当前方法的名称细节:当我们使用注解配置方法时,如果方法有参数,…...
哈希表原理,以及unordered_set/和unordered_map的封装和迭代器的实现
哈希表 unordered系列unordered_set和unordered_map的使用哈希哈希概念哈希冲突哈希函数闭散列开散列哈希表的扩容哈希表源码(开散列和闭散列) 封装unordered_set/和unordered_map,以及实现迭代器节点定义unordered_set定义unordered_map定义…...
如何把歌曲里的伴奏音乐提取出来,分享几个方法给大家!
对于一首歌,我们都知道,它有两部分组成:背景音乐人声。这两者合在一起,便是我们经常听的歌。部分用户想要直接获取歌曲伴奏,那么可以在UU伴奏网上下载。 操作方法比较简单,直接搜索想要的歌曲名称就可以了…...
区块链产业快速发展 和数集团开启区块链应用新时代
UTONMOS区块链游戏要来了。 就在5月底,UTONMOS品牌所属公司上海和数集团在泰国发布了【神念无界】系列的多款国际版链游,包括【神念无界-源起山海】、【北荒传奇】、【神宠岛】、【神农园】等区块链游戏。 以【神念无界-源起山海】为例,其是…...
初出茅庐的小李博客之常见字符串函数使用
C语言字符数组与字符串数组 在C语言中,字符数组和字符串数组实际上是同一种类型。字符串是由字符组成的字符数组,通常以空字符 ‘\0’ 结尾。C语言中的字符串是一种常见的数据类型。我们可以通过两种方式定义字符数组跟字符串数组 char charArray[10];…...
运筹学工程化流程和常见的运筹学算法分类以及常见软件
文章目录 前言运筹学工程化流程运筹学算法分类运筹学软件参考文献 前言 自2023年初新冠疫情管控放开后,各家公司各类岗位的人员都有被裁的消息传出,但用人市场上运筹学算法岗位却反其道行之,用工出现了激增。可以预见的是数据算法将从传统的…...
JAVA面向对象(三)
第三章 封装与继承 目录 第三章 封装与继承 1.1.封装 1.2.包 1.3.访问权限控制 1.4.static修饰符 1.4.1.成员变量 1.4.2.成员方法 1.4.3.代码块 总结 内容仅供学习交流,如有问题请留言或私信!!!!࿰…...
前端面试题---跨域处理和异常、错误处理
一.跨域处理 在前端开发中,当我们在浏览器中向不同域名或端口发起请求时,就会遇到跨域请求的限制。为了处理跨域请求,有几种常见的方法 1.JSONP(JSON with Padding) JSONP是一种利用 <script> 标签可以跨域加载…...
网络安全之反序列化漏洞分析
简介 FastJson 是 alibaba 的一款开源 JSON 解析库,可用于将 Java 对象转换为其 JSON 表示形式,也可以用于将 JSON 字符串转换为等效的 Java 对象分别通过toJSONString和parseObject/parse来实现序列化和反序列化。 使用 对于序列化的方法toJSONStrin…...
19 贝叶斯线性回归
文章目录 19 贝叶斯线性回归19.1 频率派线性回归19.2 Bayesian Method19.2.1 Inference问题19.2.2 Prediction问题 19 贝叶斯线性回归 19.1 频率派线性回归 数据与模型: 样本: { ( x i , y i ) } i 1 N , x i ∈ R p , y i ∈ R p {\lbrace (x_i, y_…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
【python异步多线程】异步多线程爬虫代码示例
claude生成的python多线程、异步代码示例,模拟20个网页的爬取,每个网页假设要0.5-2秒完成。 代码 Python多线程爬虫教程 核心概念 多线程:允许程序同时执行多个任务,提高IO密集型任务(如网络请求)的效率…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
C++:多态机制详解
目录 一. 多态的概念 1.静态多态(编译时多态) 二.动态多态的定义及实现 1.多态的构成条件 2.虚函数 3.虚函数的重写/覆盖 4.虚函数重写的一些其他问题 1).协变 2).析构函数的重写 5.override 和 final关键字 1&#…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...
水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...
嵌入式学习之系统编程(九)OSI模型、TCP/IP模型、UDP协议网络相关编程(6.3)
目录 一、网络编程--OSI模型 二、网络编程--TCP/IP模型 三、网络接口 四、UDP网络相关编程及主要函数 编辑编辑 UDP的特征 socke函数 bind函数 recvfrom函数(接收函数) sendto函数(发送函数) 五、网络编程之 UDP 用…...
