# pzhiqServer **Repository Path**: pzhiq/pzhiq-server ## Basic Information - **Project Name**: pzhiqServer - **Description**: node服务端-koa - **Primary Language**: NodeJS - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-04-20 - **Last Updated**: 2024-06-25 ## Categories & Tags **Categories**: Uncategorized **Tags**: 项目 ## README # 一.项目初始化 ## 1:npm初始化 ``` npm init -y ``` 生成package.json文件: - 记录项目的依赖 ## 2:git 初始化 ``` git init ``` ### 3:创建README文件 # 二. 搭建项目 ## 1:安装Koa框架 ``` npm install koa ``` ## 2:编写基础的app 创建`src/main.js` ``` const Koa = require("koa"); const app = new Koa(); app.use((ctx, next) => { ctx.body = "hello world"; }); app.listen(3333, () => { console.log("server is running on http://localhost:3333"); }); ``` ## 3:项目优化 1. 自动重启服务-----安装nodemon工具 (npm i nodemon -D)安装在开发环境里。 2. ``` //添加一个script "scripts": { "dev": "nodemon ./src/main.js", "test": "echo \"Error: no test specified\" && exit 1" }, ``` 3. 读取配置文件-->安装dotenv包, npm i dotenv(读取根目录里的`.env`文件,将配置写入process.env中) `.env`:APP_PORT=3333 4. ``` const dotenv = require("dotenv"); dotenv.config(); // console.log(process.env.APP_PORT); //process: 当前node 进程 env: 环境变变量 module.exports = process.env; ``` # 三.添加api路由 路由:根据不同的URL,调用对应处理函数 1. :安装koa-router并导入 2. 实例化对象 3. 编写路由 4. 注册中间件 ``` const Koa = require("koa"); const app = new Koa(); //1: 导入包 2: 实例化对象 3:编写路由 4: 注册中间件 // const Router = require("koa-router"); // const router = new Router(); // router.get("/", (ctx, next) => { // ctx.body = "hello world2 "; // }); // router.get("/user", (ctx, next) => { // ctx.body = "hello pzq"; // }); app.use(router.routes()) ``` # 四.目录结构整理 1:http和app拆分将框架之外的东西分离,方便后面使用其他框架比如express ``` const app = require("./app/index"); app.listen(APP_PORT, () => { console.log(`server is running on http://localhost:${APP_PORT}`); }); ``` 2: 将路由和控制器拆分 路由:解析URL,根据URL分发对应的不同控制器方法 控制器:处理每个接口逻辑 router/user.router.js ``` // 2: 实例化对象 const router = new Router({ prefix: "/users" }); const { register, login } = require("../controller/user.controller"); // 3:编写路由 // GET /users/register router.post("/register", register); // GET /users/login router.post("/login",login); ``` controller/user.controller.js ``` class UserController { async register(ctx, next) { ctx.body = "用户注册"; } async login(ctx, next) { ctx.body = "用户login"; } } module.exports = new UserController(); ``` /app/: 应用层,使用的框架和中间件 /config/:项目配置文件夹 /router/ : 路由接口层,某个类别里的所有接口 /controller/:路由接口逻辑控制层,拿到接口请求数据,跟数据库层交付,再接口返回结果 /service/:数据库服务层,真实去数据库增删改查,并返回操作结果和数据 /db/:项目数据库连接层,数据库账户密码名称、数据表等 /model/: 模型层 # 五. 解析body ### 1:安装koa-body`npm i koa-body ` ### 2:改写app.js ``` // 解析请求的json内容 const { koaBody } = require("koa-body"); // 使用koaBody中间件 app.use(koaBody()); //ctx.request.body 中有请求的数据 //在路由之前使用 ``` 3:解析请求数据 ``` async register(ctx, next) { // 获取数据 console.log(ctx.request.body); const { user_name, password } = ctx.request.body; // 操作数据库 const res = await createUser(user_name, password); console.log(res); // 返回结果 ctx.body = ctx.request.body; } ``` 4:拆分service层 主要做数据库的相关操作 ![image-20230420111721469](/Users/manbapan/Library/Application Support/typora-user-images/image-20230420111721469.png) # 六.数据库操作 Sequelize ORM数据库工具 ORM: 对象关系映射 - 数据表对应(映射)一个类 - 数据表中的数据行对应一个对象 - 数据表的字段对应对象的属性 - 数据表的操作对应对象的方法 安装依赖 mysql2是sequelize底层库,基于此封装的 `npm i mysql2 sequelize` 连接数据库 ``` const { Sequelize } = require("sequelize"); const { MYSQL_HOST, MYSQL_PORT, MYSQL_USER, MYSQL_PWD, MYSQL_DB, } = require("../config/config.default"); const seq = new Sequelize("databaseName", "userName", "password", { host: MYSQL_HOST, dialect: "mysql", }); //测试链接是否成功 seq .authenticate() .then(() => { console.log("数据库连接成功"); }) .catch((err) => { console.log("数据库连接失败", err); }); module.exports = seq; //最后把一些常量写成配置到.env ``` 1. 下载MySQL并配置好环境 2. 下载使用navicat工具创建数据库 3. 代码连接创建的数据库 4. 接口操作数据库 创建模型:用户模型User,这个创建了之后就可以在service层通过这个导出的东西操作数据库。 ``` const { DataTypes } = require("sequelize"); const seq = require("../db/seq"); // 创建模型p_user => 对应数据表名称 p_users const User = seq.define( "p_user", { // id会被sequelize自动创建 user_name: { type: DataTypes.STRING, allowNull: false, unique: true, comment: "用户名,唯一", }, password: { type: DataTypes.CHAR(64), allowNull: false, comment: "密码", }, isAdmin: { type: DataTypes.BOOLEAN, allowNull: false, defaultValue: 0, comment: "是否为管理员, 0: 不是管理员(默认), 1: 是", }, } // { // timestamps: false,//为true就自动生成创建的时间戳 默认为true // } ); // User.sync({ force: true }); //如果数据库中有这个表会强制删除重建//创建数据表 module.exports = User; ``` # 七.错误处理 使用ctx.app.emit("error", userFormatError, ctx);抛出错误 ``` if (!user_name || !password) { console.error("用户名或密码为空", ctx.request.body); // 提交错误信息 ctx.app.emit("error", userFormatError, ctx); return; } ``` userFormatError: 错误配置 ``` userFormatError: { code: '10001', message: "用户名或密码为空!!!", result: "", }, ``` 用app.on处理所有抛出的异常 ``` app.on('error',(error,ctx)=>{ let status = 500; switch (err.code) { case "10001": status = 400; break; case "10002": status = 409; break; case "10003": status = 500; break; default: break; } ctx.status = status; ctx.body = err; }) ``` # 八.加密 在将密码保存到数据库之前要对密码进行加密操作 `npm i bcryptjs ` ``` const bcrypt = require("bcryptjs"); //生成一个盐,用来加密密码 const salt = bcrypt.genSaltSync(10); // 此时hash保存的是密文 const hash = bcrypt.hashSync(password, salt); ctx.request.body.password = hash; //将此密码存到数据库 ······· //登陆比对密码 bcrypt.compareSync('登陆输入的密码', '数据库拿到的密文') ``` # 九.用户的认证token 登陆成功后,给用户颁发一个令牌token,用户在以后的每一次请求中携带这个令牌,服务端再校验是否过期。 `npm i jsonwebtoken` jwt: json web token (head:头部 payload:载荷 signature:签名) //生成token jwt.sign(payload, secretOrPrivateKey, [options, callback]) jwt.sign(res, JWT_SECRET, { expiresIn: "1d" }), 解析token jwt.verify(token, secretOrPublicKey, [options, callback])