同步操作将从 jeyarLin/KOA 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
npm init -y
git init
// 创建.gitignore文件
npm install koa
创建 src/main.js
const Koa = require("koa");
const app = new Koa();
app.use((ctx, next) => {
ctx.body = "hello api";
});
app.listen(3000, () => {
console.log("server is running on http://localhost:3000");
});
运行方式一:
node src/main.js
安装 nodemon 工具
npm i nodemon
编写 package.json 脚本
"scripts": {
"dev":"nodemon ./src/main.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
执行 'npm run dev'启动服务
安装 dotenv,读取根目录中的.env 文件,将配置写 process.env 中
npm i dotenv
创建.env 文件
APP_PORT=8000
创建 src/config/config.default.js
const dotenv = require("dotenv");
dotenv.config();
// console.log(process.env.APP_PORT);
module.exports = process.env;
改写 main.js
const Koa = require("koa");
const { APP_PORT } = require("./config/config.default");
const app = new Koa();
app.use((ctx, next) => {
ctx.body = "hello api";
});
app.listen(APP_PORT, () => {
console.log(`server is running on http://localhost:${APP_PORT}`);
});
路由:根据不同的 URL,调用对应处理函数
npm i koa-router
步骤
创建/src/router 目录,编写 user.route.js
const Router = require("koa-router");
const router = new Router({ prefix: "/users" });
// GET /users/
router.get("/", (ctx, next) => {
ctx.body = "hello users";
});
module.exports = router;
const Koa = require("koa");
const { APP_PORT } = require("./config/config.default");
const userRouter = require("./router/user.route");
const app = new Koa();
app.use(userRouter.routes());
app.listen(APP_PORT, () => {
console.log(`server is running on http://localhost:${APP_PORT}`);
});
新建 src/app/index.js
const Koa = require("koa");
const userRouter = require("../router/user.route");
const app = new Koa();
app.use(userRouter.routes());
module.exports = app;
改写 mian.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}`);
});
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/user.controller.js
class UserController {
async register(ctx, next) {
ctx.body = "注册成功";
}
async login(ctx, next) {
ctx.body = "登录成功";
}
}
module.exports = new UserController();
1 安装 koa-body
npm i koa-body
2 注册中间件
改写app/index.js
const Koa = require("koa");
const { koaBody } = require("koa-body");
const userRouter = require("../router/user.route");
const app = new Koa();
app.use(koaBody());
app.use(userRouter.routes());
module.exports = app;
3 解析请求的数据 改写controller/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 = ctx.request.body;
}
async login(ctx, next) {
// ctx.body = "登录成功";
console.log(ctx.request.body);
ctx.body = ctx.request.body;
}
}
module.exports = new UserController();
4 拆分service层 -service层主要是做数据库处理 -创建src/service/user.service.js
class UserService {
async createUser(user_name, password) {
// todo 写入数据库
return "写入数据库成功";
}
}
module.exports = new UserService();
sequelize ORM数据库工具
ORM:对象关系映射
npm i mysql2 sequelize
src/db/seq.js
const { Sequelize } = require("sequelize");
const {
MYSQL_HOST,
MYSQL_PORT,
MYSQL_USER,
MYSQL_PWD,
MYSQL_DB,
} = require("../config/config.default");
// 方法 3: 分别传递参数 (其它数据库)
const sequelize = new Sequelize(MYSQL_DB, MYSQL_USER, MYSQL_PWD, {
host: MYSQL_HOST,
// 选择一种支持的数据库:
// 'mysql', 'mariadb', 'postgres', 'mssql', 'sqlite', 'snowflake', 'db2' or 'ibmi'
dialect: "mysql",
});
sequelize
.authenticate()
.then(() => {
console.log("数据库链接成功");
})
.catch((err) => {
console.log("数据库链接失败", err);
});
// node src/db/seq.js
module.exports = sequelize;
.env
APP_PORT = 8000
MYSQL_HOST = localhost
MYSQL_PORT = 3306
MYSQL_USER = root
MYSQL_PWD = 123456
MYSQL_DB = dbtest1
sequelize主要通过Model对应数据表 创建src/model/user.model.js
const { DataTypes } = require("sequelize");
// https://www.sequelize.cn/
const seq = require("../db/seq");
// 创建模型(Model zd_user -> 表 zd_users)
const User = seq.define("zd_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:是管理员",
},
});
// 强制同步数据库(创建数据表)
// User.sync({ force: true });
// node src/model/user.model.js
module.exports = User;
所有数据库的操作都在 Service 层完成, Service 调用 Model 完成数据库操作
改写src/service/user.service.js
const User = require("../model/user.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 });
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 = "登录成功";
console.log(ctx.request.body);
ctx.body = ctx.request.body;
}
}
module.exports = new UserController();
在控制器中, 对不同的错误进行处理, 返回不同的提示错误提示, 提高代码质量
src/controller/user.controller.js
const { createUser, getUserInfo } = 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 (getUserInfo({ 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 = "登录成功";
console.log(ctx.request.body);
ctx.body = ctx.request.body;
}
}
module.exports = new UserController();
在service中封装函数getUserInfo
const User = require("../model/user.model");
class UserService {
async createUser(user_name, password) {
// 插入数据
// await表达式:promise对象的值
const res = await User.create({ user_name, password });
return res.dataValues;
}
async getUserInfo({ 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 { getUserInfo } = require("../service/user.service");
const { userFormateError, userAlreadyExisted } = 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;
// 合理性 deferent
const res = await getUserInfo({ user_name });
if (res) {
console.error("用户名已经存在", { user_name });
ctx.app.emit("error", userAlreadyExisted, ctx);
return;
}
await next();
};
module.exports = { userValidator, verifyUser };
ctx.app.emit
提交错误app.on
监听编写统一的错误定义文件
src/constant/err.type.js
module.exports = {
userFormateError: {
code: '10001',
message: '用户名或密码为空',
result: '',
},
userAlreadyExisted: {
code: '10002',
message: '用户已经存在',
result: '',
},
}
src/app/errHandler.js
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
src/middleware/user.middleware.js
const bcrypt = require('bcryptjs');
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()
}
改写user.router.js
const {
userValidator,
verifyUser,
crpytPassword,
} = require("../middleware/user.middleware");
// 注册接口
router.post("/register", userValidator, verifyUser, crpytPassword, register);
改写 src/middleware/user.middleware.js
const {
userDoesNotExist,
invalidPassword,
userLoginError,
} = require("../constant/err.type");
const verifyLogin = async (ctx, next) => {
// 1. 判断用户是否存在(不存在:报错)
const { user_name, password } = ctx.request.body;
try {
const res = await getUserInfo({ 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 };
定义错误类型 src/constant/err.type.js
module.exports = {
userFormateError: {
code: '10001',
message: '用户名或密码为空',
result: '',
},
userAlreadyExisted: {
code: '10002',
message: '用户已经存在',
result: '',
},
userRegisterError: {
code: '10003',
message: '用户注册错误',
result: '',
},
userDoesNotExist: {
code: '10004',
message: '用户不存在',
result: '',
},
userLoginError: {
code: '10005',
message: '用户登录失败',
result: '',
},
invalidPassword: {
code: '10006',
message: '密码不匹配',
result: '',
},
}
改写路由
// 登录接口
router.post('/login', userValidator, verifyLogin, login)
登录成功后, 给用户颁发一个令牌 token, 用户在以后的每一次请求中携带这个令牌.
jwt: jsonwebtoken
npm i jsonwebtoken
async login(ctx, next) {
const { user_name } = ctx.request.body
// 1. 获取用户信息(在token的payload中, 记录id, user_name, is_admin)
try {
// 从返回结果对象中剔除password属性, 将剩下的属性放到res对象
const { password, ...res } = await getUserInfo({ user_name })
ctx.body = {
code: 0,
message: '用户登录成功',
result: {
token: jwt.sign(res, JWT_SECRET, { expiresIn: '1d' }),
},
}
} catch (err) {
console.error('用户登录失败', err)
}
}
在.env
定义
JWT_SECRET = xzd
const jwt = require('jsonwebtoken')
const { JWT_SECRET } = require('../config/config.default')
const { tokenExpiredError, invalidToken } = require('../constant/err.type')
const auth = async (ctx, next) => {
const { authorization } = ctx.request.header
const token = authorization.replace('Bearer ', '')
console.log(token)
try {
// user中包含了payload的信息(id, user_name, is_admin)
const user = jwt.verify(token, JWT_SECRET)
ctx.state.user = user
} catch (err) {
switch (err.name) {
case 'TokenExpiredError':
console.error('token已过期', err)
return ctx.app.emit('error', tokenExpiredError, ctx)
case 'JsonWebTokenError':
console.error('无效的token', err)
return ctx.app.emit('error', invalidToken, ctx)
}
}
await next()
}
module.exports = {
auth,
}
// 修改密码接口
router.patch('/', auth, (ctx, next) => {
console.log(ctx.state.user)
ctx.body = '修改密码成功'
})
新的内容
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。