1 Star 0 Fork 0

coder / DataServer

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

c一 项目初始化

1.npm初始化

npm init -y  生成package.json  文件

2.git 初始化

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

二 搭建项目

1.安装koa

npm install koa --save

2.编写最基本的app

创建 src/main.js

3.测试

node ./src/main.js

三.项目的基本优化

1.自动重启服务

npm i nodemon

编写package.json 运行脚本

"script":{
    "start":"nodemon ./src/main.js"
}

执行 npm start 启动服务

2.读取配置文件

安装 dotenv

npm i dotenv 

创建.enc文件

APP_PORT = 8000

创建src/config/config.default.js

四.添加路由

路由:根据不同的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' });

router.get('/', function (ctx, next) {
    ctx.body = 'hello user'
});

module.exports = router;

3.改写main.js

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

五.目录结构优化

1.将 http 服务 和 app业务 拆分

创建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}`);
})

2.将路由和控制器拆分

路由:解析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();

六.解析body

1.安装 koa-body

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:对象关系映射

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

1.安装 sequelize

npm i mysql2 sequelize --save

2.连接数据库

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

3.编写配置文件

APP_PORT = 8000

MYSQL_HOST = 127.0.0.1

MYSQL_PORT = 3306

MYSQL_USER = root

MYSQL_PWD = root

MYSQL_DB = dataserver

八.创建User模型

1.拆分Model层

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

十一. 拆分中间件

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

image-20230321223918833

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监听

编写统一的错误定义文件

app/errHandler.js

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 salt = bcrypt.genSaltSync(10)
  // hash保存的是 密文
  const hash = bcrypt.hashSync(password, salt)

  ctx.request.body.password = hash

  await next()
}

3 在 router 中使用

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

空文件

简介

一个以 node.js + koa 框架搭建的后台服务 展开 收起
NodeJS 等 3 种语言
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
NodeJS
1
https://gitee.com/libaile/data-server.git
git@gitee.com:libaile/data-server.git
libaile
data-server
DataServer
master

搜索帮助