c一 项目初始化
npm init -y 生成package.json 文件
git init 生成.git 隐藏文件,git的本地仓库
npm install koa --save
创建 src/main.js
node ./src/main.js
npm i nodemon
编写package.json 运行脚本
"script":{
"start":"nodemon ./src/main.js"
}
执行 npm start
启动服务
安装 dotenv
npm i dotenv
创建.enc文件
APP_PORT = 8000
创建src/config/config.default.js
路由:根据不同的URL,调用对应处理函数
npm i koa-router
步骤:
1.导入包
2.实例化对象
3.编写路由
4.注册中间件
创建 src/router
目录,编写user.route.js
const Router = require('koa-router');
const router = new Router({ prefix:'/users' });
router.get('/', function (ctx, next) {
ctx.body = 'hello user'
});
module.exports = router;
const Koa = require('koa');
const app = new Koa();
const userRouter = require('./router/user.route');
const { APP_PORT } = require('./config/config.default');
app
.use(userRouter.routes())
app.listen(3000, () => {
console.log(`server is running on http://localhost:${APP_PORT}`);
})
创建src/app/index.js
const Koa = require('koa');
const app = new Koa();
const userRouter = require('../router/user.route');
app.use(userRouter.routes());
module.exports = app;
改写 main .js
const { APP_PORT } = require('./config/config.default');
const app = require('./app');
app.listen(APP_PORT, () => {
console.log(`server is running on http://localhost:${APP_PORT}`);
})
路由:解析URL,分发给控制器对应的方法
const Router = require('koa-router');
const { register, login } = require('../controller//user.controller');
const router = new Router({ prefix:'/users' });
// 注册接口
router.post('/register', register);
// 登录接口
router.post('/login', login);
module.exports = router;
控制器:处理不同的业务
创建 controller/users.controller.js
class UserController {
async register(ctx, next) {
ctx.body = { iRet:0, message:'用户注册成功'};
}
async login(ctx, next) {
ctx.body = { iRet:0, message:'用户登录成功'};
}
}
module.exports = new UserController();
npm i koa-body
```
### 2.注册中间件
改写 app/index.js
![image-20230319150351909](C:\Users\baileli\AppData\Roaming\Typora\typora-user-images\image-20230319150351909.png)
### 3.解析请求数据
改写 `user.controller.js`
```
const { createUser } = require('../service/user.service');
class UserController {
async register(ctx, next) {
// 1.获取数据
const { user_name, password } = ctx.request.body;
// 2.操作数据库
const res = await createUser(user_name, password);
// 3.返回结果
ctx.body = res;
}
async login(ctx, next) {
ctx.body = { iRet:0, message:'用户登录成功'};
}
}
module.exports = new UserController();
```
### 4.拆分 service 层
service层主要是做数据库处理
```
class UserService {
async createUser(user_name, password) {
// tode:写入数据库
return '写入数据库成功'
}
}
module.exports = new UserService();
sequelize ORM 数据库工具
ORM:对象关系映射
npm i mysql2 sequelize --save
创建 src/db/req.js
const {
MYSQL_HOST,
MYSQL_PORT,
MYSQL_USER,
MYSQL_PWD ,
MYSQL_DB
} = require('../config/config.default')
const { Sequelize } = require('sequelize');
const seq = new Sequelize(MYSQL_DB, MYSQL_USER, MYSQL_PWD, {
localhost: MYSQL_HOST + MYSQL_PORT,
dialect: 'mysql'
});
// 测试数据库是否连接成功
seq.authenticate().then( () => {
console.log('数据库连接成功')
}).catch((err) => {
console.log(err)
})
module.exports = seq;
APP_PORT = 8000
MYSQL_HOST = 127.0.0.1
MYSQL_PORT = 3306
MYSQL_USER = root
MYSQL_PWD = root
MYSQL_DB = dataserver
const { DataTypes } = require('sequelize');
const seq = require('../db/seq');
// 创建模型 (Model data_user => data_users)
const User = seq.define('data_user', {
// id 会被 sequelize 自动创建、管理
user_name: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
comment: '用户名,唯一'
},
password: {
type: DataTypes.CHAR(64),
allowNull: false,
comment: '用户密码'
},
is_admin: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: 0,
comment: '是否为管理员 0 非管理员(默认) 1 管理员'
},
}, {
// timestamps: false // 是否生成事件戳
});
// 强制同步数据库(创建数据表)
// User.sync({ force: true });
module.exports = User;
所有数据库的操作都在 Service 层完成, Service 调用 Model 完成数据库操作
改写src/service/user.service.js
const User = require('../model/use.model')
class UserService {
async createUser(user_name, password) {
// 插入数据
// User.create({
// // 表的字段
// user_name: user_name,
// password: password
// })
// await表达式: promise对象的值
const res = await User.create({ user_name, password })
// console.log(res)
return res.dataValues
}
}
module.exports = new UserService()
同时, 改写user.controller.js
const { createUser } = require('../service/user.service')
class UserController {
async register(ctx, next) {
// 1. 获取数据
// console.log(ctx.request.body)
const { user_name, password } = ctx.request.body
// 2. 操作数据库
const res = await createUser(user_name, password)
// console.log(res)
// 3. 返回结果
ctx.body = {
code: 0,
message: '用户注册成功',
result: {
id: res.id,
user_name: res.user_name,
},
}
}
async login(ctx, next) {
ctx.body = '登录成功'
}
}
module.exports = new UserController()
在控制器中, 对不同的错误进行处理, 返回不同的提示错误提示, 提高代码质量
const { createUser, getUerInfo } = require('../service/user.service')
class UserController {
async register(ctx, next) {
// 1. 获取数据
// console.log(ctx.request.body)
const { user_name, password } = ctx.request.body
// 合法性
if (!user_name || !password) {
console.error('用户名或密码为空', ctx.request.body)
ctx.status = 400
ctx.body = {
code: '10001',
message: '用户名或密码为空',
result: '',
}
return
}
// 合理性
if (getUerInfo({ user_name })) {
ctx.status = 409
ctx.body = {
code: '10002',
message: '用户已经存在',
result: '',
}
return
}
// 2. 操作数据库
const res = await createUser(user_name, password)
// console.log(res)
// 3. 返回结果
ctx.body = {
code: 0,
message: '用户注册成功',
result: {
id: res.id,
user_name: res.user_name,
},
}
}
async login(ctx, next) {
ctx.body = '登录成功'
}
}
module.exports = new UserController()
在 service 中封装函数
const User = require('../model/use.model')
class UserService {
async createUser(user_name, password) {
// 插入数据
// await表达式: promise对象的值
const res = await User.create({ user_name, password })
// console.log(res)
return res.dataValues
}
async getUerInfo({ id, user_name, password, is_admin }) {
const whereOpt = {}
id && Object.assign(whereOpt, { id })
user_name && Object.assign(whereOpt, { user_name })
password && Object.assign(whereOpt, { password })
is_admin && Object.assign(whereOpt, { is_admin })
const res = await User.findOne({
attributes: ['id', 'user_name', 'password', 'is_admin'],
where: whereOpt,
})
return res ? res.dataValues : null
}
}
module.exports = new UserService()
为了使代码的逻辑更加清晰, 我们可以拆分一个中间件层, 封装多个中间件函数
添加src/middleware/user.middleware.js
const { getUerInfo } = require('../service/user.service')
const { userFormateError, userAlreadyExited } = require('../constant/err.type')
const userValidator = async (ctx, next) => {
const { user_name, password } = ctx.request.body
// 合法性
if (!user_name || !password) {
console.error('用户名或密码为空', ctx.request.body)
ctx.app.emit('error', userFormateError, ctx)
return
}
await next()
}
const verifyUser = async (ctx, next) => {
const { user_name } = ctx.request.body
if (getUerInfo({ user_name })) {
ctx.app.emit('error', userAlreadyExited, ctx)
return
}
await next()
}
module.exports = {
userValidator,
verifyUser,
}
ctx.app.emit
提交错误app.on
监听编写统一的错误定义文件
app/errHandler.js
module.exports = {
userFormateError: {
code: '10001',
message: '用户名或密码为空',
result: '',
},
userAlreadyExited: {
code: '10002',
message: '用户已经存在',
result: '',
},
}
module.exports = (err, ctx) => {
let status = 500
switch (err.code) {
case '10001':
status = 400
break
case '10002':
status = 409
break
default:
status = 500
}
ctx.status = status
ctx.body = err
}
改写app/index.js
const errHandler = require('./errHandler')
// 统一的错误处理
app.on('error', errHandler)
在将密码保存到数据库之前, 要对密码进行加密处理
123123abc (加盐) 加盐加密
npm i bcryptjs
const salt = bcrypt.genSaltSync(10)
// hash保存的是 密文
const hash = bcrypt.hashSync(password, salt)
ctx.request.body.password = hash
await next()
}
改写user.route.j
const Router = require('koa-router')
const {
userValidator,
verifyUser,
crpytPassword,
} = require('../middleware/user.middleware')
const { register, login } = require('../controller/user.controller')
const router = new Router({ prefix: '/users' })
// 注册接口
router.post('/register', userValidator, verifyUser, crpytPassword, register)
// 登录接口
router.post('/login', login)
module.exports = router
流程:
改写src/middleware/user.middleware.js
const bcrypt = require('bcryptjs')
const { getUerInfo } = require('../service/user.service')
const {
userFormateError,
userAlreadyExited,
userRegisterError,
userDoesNotExist,
userLoginError,
invalidPassword,
} = require('../constant/err.type')
const userValidator = async (ctx, next) => {
const { user_name, password } = ctx.request.body
// 合法性
if (!user_name || !password) {
console.error('用户名或密码为空', ctx.request.body)
ctx.app.emit('error', userFormateError, ctx)
return
}
await next()
}
const verifyUser = async (ctx, next) => {
const { user_name } = ctx.request.body
// if (await getUerInfo({ user_name })) {
// ctx.app.emit('error', userAlreadyExited, ctx)
// return
// }
try {
const res = await getUerInfo({ user_name })
if (res) {
console.error('用户名已经存在', { user_name })
ctx.app.emit('error', userAlreadyExited, ctx)
return
}
} catch (err) {
console.error('获取用户信息错误', err)
ctx.app.emit('error', userRegisterError, ctx)
return
}
await next()
}
const crpytPassword = async (ctx, next) => {
const { password } = ctx.request.body
const salt = bcrypt.genSaltSync(10)
// hash保存的是 密文
const hash = bcrypt.hashSync(password, salt)
ctx.request.body.password = hash
await next()
}
const verifyLogin = async (ctx, next) => {
// 1. 判断用户是否存在(不存在:报错)
const { user_name, password } = ctx.request.body
try {
const res = await getUerInfo({ user_name })
if (!res) {
console.error('用户名不存在', { user_name })
ctx.app.emit('error', userDoesNotExist, ctx)
return
}
// 2. 密码是否匹配(不匹配: 报错)
if (!bcrypt.compareSync(password, res.password)) {
ctx.app.emit('error', invalidPassword, ctx)
return
}
} catch (err) {
console.error(err)
return ctx.app.emit('error', userLoginError, ctx)
}
await next()
}
module.exports = {
userValidator,
verifyUser,
crpytPassword,
verifyLogin,
}
定义错误类型
module.exports = {
userFormateError: {
code: '10001',
message: '用户名或密码为空',
result: '',
},
userAlreadyExited: {
code: '10002',
message: '用户已经存在',
result: '',
},
userRegisterError: {
code: '10003',
message: '用户注册错误',
result: '',
},
userDoesNotExist: {
code: '10004',
message: '用户不存在',
result: '',
},
userLoginError: {
code: '10005',
message: '用户登录失败',
result: '',
},
invalidPassword: {
code: '10006',
message: '密码不匹配',
result: '',
},
}
改写路由 user.route.js
router.post('/login', userValidator, verifyLogin, login)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。