零、项目结构
main.js是项目的入口文件,导入了config/config.default.js文件和app/index.js文件。
config/config.default.js文件是读取你的.env文件,将配置写到process.env中
.env 全局默认配置文件
app/index.js是项目的app服务,导入了koa框架,koa-body并使用,app/errHandler 错误处理中间件和router/user.router路由模块
router/user.router是项目的路由文件,导入使用了koa-router,middleware/user.middleware和controller/user.controller
controller/user.controller是项目的控制器,用于接口的编写,导入了一个service/user.service和consitant/err.type
service/user.service主要是做数据库处理,导入了model/user.model,调用model完成数据库操作
model/user.model 编写数据表,导入了db/seq连接数据库,用于service/user.service数据库处理
db/seq连接数据库,导入了.env 全局默认配置文件
middleware/user.middleware为中间件文件,用于处理服务请求的细节,导入了service/user.service和constant/err.type
constant/err.type为统一错误定义的文件,定义一些请求中的错误原因
app/errHandler为错误处理函数,需要在app服务中on()绑定错误
一、项目初始化
1.npm初始化
生成package.json文件
2.git初始化
生成.git隐藏文件夹, git 的本地仓库
二、搭建项目
1.安装Koa框架
2.编写最基本的app
新建一个/src/main.js文件
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | 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");
 })
 
 | 
3.测试
在终端,使用命令
或者有安装 nodemon
浏览器显示 hello api
三、项目的基本优化
1.自动重启服务
全局安装nodemon工具
编写package.json脚本
| 12
 3
 4
 
 | "scripts": {"dev": "nodemon ./src/main.js",
 "test": "echo \"Error: no test specified\" && exit 1"
 },
 
 | 
执行npm run dev启动服务
2.读取配置文件
安装dotenv, 读取根目录中的.env文件, 将配置写到process.env中
在项目根目录中创建.env文件
创建src/config/config.default.js
| 12
 3
 4
 5
 6
 7
 
 | const dotenv = require('dotenv')
 dotenv.config()
 
 
 
 module.exports = process.env
 
 | 
修改main.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | 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, 调用对应处理函数
1.安装koa-router
步骤:
- 导入包
- 实例化对象
- 编写路由
- 注册中间件
2.编写路由
创建src/router目录, 编写user.router.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | const Router = require('koa-router')
 
 const router = new Router({ prefix: '/users'})
 
 router.get('/',(ctx,next)=>{
 ctx.body = 'hello users'
 })
 
 module.exports = router
 
 | 
3.修改main.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | const Koa = require('koa')
 const {APP_PORT} = require('./config/config.default')
 
 const userRouter = require('./router/user.router')
 
 const app = new Koa()
 
 app.use(userRouter.routes())
 
 app.listen(APP_PORT,()=>{
 console.log(`server is running on http://localhost:${APP_PORT}`);
 })
 
 | 
五、目录结构优化
1.将 http 服务和 app 服务拆分
创建src/app/index.js文件
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | const Koa = require('koa')
 const userRouter = require('../router/user.router')
 
 const app = new Koa()
 
 app.use(userRouter.routes())
 
 module.exports = app
 
 | 
修改main.js
| 12
 3
 4
 5
 6
 7
 
 | 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}`);
 })
 
 | 
2.将路由和控制器拆分
路由: 解析 URL, 分布给控制器对应的方法
控制器: 处理不同的业务
修改user.route.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 
 | 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
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | class UserController {async register(ctx, next) {
 ctx.body = '用户注册成功'
 }
 
 async login(ctx, next) {
 ctx.body = '登录成功'
 }
 }
 
 module.exports = new UserController()
 
 | 
六. 解析 body
1.安装 koa-body
2.注册中间件
改写app/index.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | const Koa = require('koa')
 const KoaBody = require('koa-body')
 const userRouter = require('../router/user.router')
 
 const app = new Koa()
 
 
 app.use(KoaBody())
 
 app.use(userRouter.routes())
 
 module.exports = app
 
 | 
3.解析请求数据
修改user.controller.js文件
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 | const { createUser } = require('../service/user.service')
 class UserController {
 async register(ctx, next) {
 
 
 const { user_name, password } = ctx.request.body
 
 const res = await createUser(user_name, password)
 console.log(res);
 
 ctx.body = ctx.request.body
 }
 
 async login(ctx, next) {
 ctx.body = '登录成功'
 }
 }
 
 module.exports = new UserController()
 
 | 
4.拆分 service 层
service 层主要是做数据库处理
创建src/service/user.service.js
| 12
 3
 4
 5
 6
 7
 8
 
 | class UserService {async createUser(user_name,password){
 
 return '写入数据库成功'
 }
 }
 
 module.exports = new UserService()
 
 | 
七. 数据库操作
sequelize ORM 数据库工具
ORM: 对象关系映射
- 数据表映射(对应)一个类
- 数据表中的数据行(记录)对应一个对象
- 数据表字段对应对象的属性
- 数据表的操作对应对象的方法
1.安装 Sequelize 和 MySQL
2.连接数据库
新建src/db/seq.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | const { Sequelize } = require('sequelize')const { MYSQL_HOST,
 MYSQL_PORT,
 MYSQL_USER,
 MYSQL_PWD,
 MYSQL_DB } = require('../config/config.default')
 
 const seq = new Sequelize(MYSQL_DB, MYSQL_USER, MYSQL_PWD, {
 host: MYSQL_HOST,
 dialect: 'mysql'
 })
 
 seq.authenticate().then(() => {
 console.log('数据库连接成功');
 }).catch((err) => {
 console.log('数据库连接失败', err);
 })
 
 | 
3.编写配置文件
修改.env
| 12
 3
 4
 5
 6
 7
 
 | APP_PORT = 8000
 MYSQL_HOST = localhost
 MYSQL_PORT = 3306
 MYSQL_USER = root
 MYSQL_PWD = 123456
 MYSQL_DB = zdsc
 
 | 
八. 创建 User 模型
拆分 Model 层
sequelize 主要通过 Model 对应数据表
创建src/model/user.model.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 
 | const {DataTypes} = require('sequelize')const seq = require('../db/seq')
 
 
 const User = seq.define('zd_user',{
 
 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:是'
 },
 },
 
 
 
 )
 
 
 
 module.exports = User
 
 | 
九. 添加用户入库
所有数据库的操作都在 Service 层完成, Service 调用 Model 完成数据库操作
修改src/service/user.service.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | const User = require('../model/user.model')class UserService {
 async createUser(user_name, password) {
 
 
 
 const res = await User.create({
 user_name, password
 })
 
 return res.dataValues
 }
 }
 
 module.exports = new UserService()
 
 | 
修改user.controller.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 
 | const { createUser } = require('../service/user.service')
 class UserController {
 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 = {
 code:200,
 message:'用户注册成功',
 result:{
 id:res.id,
 user_name:res.user_name
 }
 }
 }
 
 async login(ctx, next) {
 ctx.body = '登录成功'
 }
 }
 
 module.exports = new UserController()
 
 | 
十. 错误处理
在控制器中, 对不同的错误进行处理, 返回不同的提示错误提示, 提高代码质量
user.controller.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 
 | const { createUser,getUserInfo } = require('../service/user.service')
 class UserController {
 async register(ctx, next) {
 
 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: '400',
 message: '用户名或密码为空',
 result: ''
 }
 return
 }
 
 if(getUserInfo({user_name})){
 ctx.status = 409,
 ctx.body = {
 code:'409',
 message: '用户已经存在',
 result:''
 }
 return
 }
 
 const res = await createUser(user_name, password)
 
 
 ctx.body = {
 code:200,
 message:'用户注册成功',
 result:{
 id:res.id,
 user_name:res.user_name
 }
 }
 }
 
 async login(ctx, next) {
 ctx.body = '登录成功'
 }
 }
 
 module.exports = new UserController()
 
 | 
在 service 中封装函数
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 
 | const User = require('../model/user.model')class UserService {
 async createUser(user_name, password) {
 
 
 
 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()
 
 | 
十一、拆分中间件
- 在出错的地方使用ctx.app.emit提交错误
- 在 app 中通过app.on监听
1.拆分中间件
添加src/middleware/user.middleware.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 
 | const { getUserInfo } = require('../service/user.service')const { userFormateError, userAlreadyExited,userRegisterError} = require('../consitant/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 userVerify = async (ctx, next) => {
 const { user_name } = ctx.request.body
 
 
 
 
 
 try {
 const res = await getUserInfo({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()
 }
 module.exports = { userValidator, userVerify }
 
 | 
2.统一错误处理
编写统一的错误定义文件
新建src/consitant/err.type.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | module.exports = {userFormateError:{
 code: '400',
 message: '用户名或密码为空',
 result:''
 },
 userAlreadyExited:{
 code: '409',
 message: '用户已存在',
 result:''
 },
 userRegisterError:{
 code: '400',
 message: '用户注册失败',
 result:''
 }
 }
 
 | 
3.错误处理函数
新建src/app/errHandler.js
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | module.exports = (err,ctx) => {let status = 500
 switch (err.code) {
 case '400':
 status = 400
 break;
 case '409':
 status = 409
 break;
 default:
 status = 500
 }
 ctx.status = status
 ctx.body = err
 }
 
 | 
修改app/index.js
| 12
 3
 
 | const errHandler = require('./errHandler')
 app.on('error', errHandler)
 
 |