node koa

Posted Jay_帅小伙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了node koa相关的知识,希望对你有一定的参考价值。

一. 项目的初始化

1 npm 初始化

npm init -y

生成package.json文件:

  • 记录项目的依赖

2 git 初始化

git init

生成’.git’隐藏文件夹, git 的本地仓库

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(3000, () => 
  console.log('server is running on http://localhost:3000')
)

3 测试

在终端, 使用node src/main.js

三. 项目的基本优化

1 自动重启服务

安装 nodemon 工具

npm i nodemon -D

编写package.json脚本

"scripts": 
  "dev": "nodemon ./src/main.js",
  "test": "echo \\"Error: no test specified\\" && exit 1"
,

执行npm run dev启动服务

2 读取配置文件

安装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, 调用对应处理函数

1 安装 koa-router

npm i koa-router

步骤:

  1. 导入包
  2. 实例化对象
  3. 编写路由
  4. 注册中间件

2 编写路由

创建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

3 改写 main.js

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`)
)

五. 目录结构优化

1 将 http 服务和 app 业务拆分

创建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

改写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`)
)

2 将路由和控制器拆分

路由: 解析 URL, 分布给控制器对应的方法

控制器: 处理不同的业务

改写user.route.js

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()

六. 解析 body

1 安装 koa-body

npm i koa-body

2 注册中间件

改写app/index.js

3 解析请求数据

改写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 = '登录成功'
  


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()

七. 集成 sequlize

sequelize ORM 数据库工具

ORM: 对象关系映射

  • 数据表映射(对应)一个类
  • 数据表中的数据行(记录)对应一个对象
  • 数据表字段对应对象的属性
  • 数据表的操作对应对象的方法

1 安装 sequelize

npm i mysql2 sequelize

2 连接数据库

src/db/seq.js

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)
  )

module.exports = seq

3 编写配置文件

APP_PORT = 8000

MYSQL_HOST = localhost
MYSQL_PORT = 3306
MYSQL_USER = root
MYSQL_PWD = 123456
MYSQL_DB = zdsc

八. 创建 User 模型

1 拆分 Model 层

sequelize 主要通过 Model 对应数据表

创建src/model/user.model.js

const  DataTypes  = require('sequelize')

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 )

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()

十一. 拆分中间件

为了使代码的逻辑更加清晰, 我们可以拆分一个中间件层, 封装多个中间件函数

1 拆分中间件

添加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,

2 统一错误处理

  • 在出错的地方使用ctx.app.emit提交错误
  • 在 app 中通过app.on监听

编写统一的错误定义文件

module.exports = 
  userFormateError: 
    code: '10001',
    message: '用户名或密码为空',
    result: '',
  ,
  userAlreadyExited: 
    code: '10002',
    message: '用户已经存在',
    result: '',
  ,

3 错误处理函数

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 (加盐) 加盐加密

1 安装 bcryptjs

npm i bcryptjs

2 编写加密中间件

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()

3 在 router 中使用

改写user.router.js

const Router = require('koa-router')

const 
  userValidator,
  verifyUser,
  crpytPassword,
 node-koa-路由传值

koa2-1

深入node.js koa原理,实现koa

深入node.js koa原理,实现koa

node中从express到koa再到koa2的发展历程

最新Node.js框架:Koa 2 实用入门