通过koa来实现本地mongoose接口

426 阅读5分钟

搭建一个基础的koa服务

nodemon的简介:www.jianshu.com/p/f60e14db0…

npm init -y
npm install koa koa-router -S
/** nodemon是一种工具,可以自动检测到目录中的文件更改时通过重新启动应用程序来调试基于node.js的应用	* 程序。
	*/
npm install nodemon -D 

创建app.js

const Koa = require('koa');
const Router = require('koa-router');

//koa实例化
const app = new Koa();
const router = new Router();

router.get('/home',async ctx=>{
  ctx.body="hello World111";
})

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000,()=>{
  console.log('服务启动了')
})

package.json需要添加一条nodemon的命令,第8行

{
  "name": "koasimple",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "server":"nodemon app.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "koa": "^2.13.1",
    "koa-router": "^10.0.0"
  },
  "devDependencies": {
    "nodemon": "^2.0.12"
  }
}

基础koa服务的目录结构

3、拆分路由模块

  1. 创建一个routes的文件夹
  2. 创建一个user.js
const Koa = require('koa');
const Router = require('koa-router');

const user = require('./routes/user.js')

//koa实例化
const app = new Koa();
const router = new Router();
// 总路由添加前缀/api,总地址变为http://localhost:3000/api
router.prefix('/api')

router.get('/',async ctx=>{
  ctx.body="hello World111";
})


// 子路由添加前缀/users,最后访问地址变为http://localhost:3000/api/users/user
router.use('/users',user.routes());

app.use(router.routes()).use(router.allowedMethods());

app.listen(3000,()=>{
  console.log('服务启动了')
})
const router = require('koa-router')();
 
router.get('/user', (ctx) => {
  ctx.body = {name:'tanwei'};
})
 
router.get('/userlist', (ctx) => {
  ctx.body = [{name:'tanwei'},{name:'weiwei'}]
})
 
module.exports = router;

4、拆分controllers

/controllers/user.js

class Users {
  async getUser(ctx){
    	ctx.body = {name:'tanwei'};
  }
  async getUserlist(ctx){
    	ctx.body = [{name:'tanwei'},{name:'weiwei'}];
  }
}

module.exports = new Users();

/routes/user.js

const router = require('koa-router')();
const { getUser,getUserlist } = require('../controllers/user.js') 
router.get('/user', (ctx) => {
  ctx.body = {name:'tanwei'};
})
 
router.get('/userlist', (ctx) => {
  ctx.body = [{name:'tanwei'},{name:'weiwei'}]
})
 
module.exports = router;

5、post请求如何处理

npm install koa-body -S

const koaBody = require('koa-body');

app.use(koaBody())


//通过ctx.request.body获取post参数

6、koa读写json文件

const fs = require('fs');
const path = require('path');
const router = require('koa-router')();


// 查询
router.get('/find', async (ctx) => {
  // 两种查询方式 1.id为空 => 查询全部  2.id有值 => 查询单个
      let id = ctx.request.query.id
      let findJson = () => {
          return new Promise((resolve,reject)=>{
              fs.readFile(path.join(__dirname, '../data/user.json'),function(err,data){
                  if(err){
                      resolve({code: -1, msg: '查询失败' + err})
                      return console.error(err);
                  }
                  let jsonData = data.toString();//将二进制的数据转换为字符串
                  jsonData = JSON.parse(jsonData);//将字符串转换为json对象
                  // 有id值=>单个 无id值=>全部
                  if (id) {
                    jsonData = jsonData.filter((item) => item.id === id);
                      // jsonData = jsonData.filter((item) => {return item.id === id});
                      resolve({code: 0, data: jsonData})
                  } else {
                      resolve({code: 0, data: jsonData})
                  }
   
              })
          })
      }
      ctx.body = await findJson()
  })

//新增和修改
router.post('/add-modify', async (ctx) => {
//  这里使用的body来解析post请求传来的数据,id是用来查找之前有的数据并进行修改,新增数据的在前台应该将id设置为空
    let id = ctx.request.body.id
    let params = ctx.request.body.params
    let writeJson = () => {
        return new Promise((resolve,reject)=>{
        // fs模块读取json文件  对fs、path模块不熟悉的可以去查下官方文档
            fs.readFile(path.join(__dirname, '../data/user.json'),function(err,data){
                if(err){
                // 报错返回
                    resolve({code: -1, msg: '新增失败' + err})
                    return console.error(err);
                }
                let jsonData = data.toString();//将二进制的数据转换为字符串
                jsonData = JSON.parse(jsonData);//将字符串转换为json对象
                // 有id值=>修改 无id值=>新增
                if (id) {
                    jsonData.splice(jsonData.findIndex(item => item.id === id), 1, params)
                } else {
                // 有重复 => 返回-1  无重复 => 将params加到json数组末尾
                    let hasRepeat = jsonData.filter((item) => item.id === params.id);
                    hasRepeat.length>0 ? resolve({code: -1, msg: '新增失败,有重复项目id'}) : jsonData.push(params);
                }
                //因为nodejs的写入文件只认识字符串或者二进制数,所以把json对象转换成字符串重新写入json文件中
                let str = JSON.stringify(jsonData,null,2);
                // let str = JSON.stringify(jsonData);
                fs.writeFile(path.join(__dirname, '../data/user.json'),str,function(err){
                    if(err){
                        resolve({code: -1, msg: '新增失败' + err})
                    }
                    resolve({code: 0, msg: '新增成功'})
                })
            })
        })
    }
    // 返回给前端
    ctx.body = await writeJson()
})

//删除
router.get('/delete', async (ctx) => {
  let id = ctx.request.query.id
  let deleteJson = () => {
      return new Promise((resolve,reject)=>{
          fs.readFile(path.join(__dirname, '../data/user.json'),function(err,data){
              if(err){
                  resolve({code: -1, msg: '删除失败' + err})
                  return console.error(err);
              }
              let jsonData = data.toString();//将二进制的数据转换为字符串
              jsonData = JSON.parse(jsonData);//将字符串转换为json对象
              // 过滤出所存item的id和前端传来id不等的 item ,下面提供了两种方法filter和splice
              jsonData = jsonData.filter((item) => item.id !== id);
              // jsonData.splice(jsonData.findIndex(item => item.id === id), 1)
              let str = JSON.stringify(jsonData,null,2);
              fs.writeFile(path.join(__dirname, '../data/user.json'),str,function(err){
                  if(err){
                      resolve({code: -1, msg: '删除失败' + err})
                  }
                  resolve({code: 0, msg: '删除成功'})
              })
          })
      })
  }
  ctx.body = await deleteJson()
})

module.exports = router;

7、koa连接mongodb数据库

//安装插件
npm install mongoose --save

在app.js这添加连接数据库的代码

const mongoose = require('mongoose');
// 连接数据库
mongoose.connect(connectionStr,(err)=>{
  if(err) {console.log('mongonDB连接失败了'); return};
  console.log('mongonDB连接成功了');
})

连接数据库的字符串抽取到了config.js文件里

module.exports = {
  connectionStr:'数据库链接码'
}

生成一个User模板,这个模板就是你定义的字段和字段的数据类型

首先我们分析一个用户会包含哪些属性

姓名、年龄、邮箱等等

用Schema生成model

/models/user.js

//models/user.js
const mongoose = require('mongoose');
const { Schema, model } = mongoose;

const userSchema = new Schema({
  name:{type:String,required:true},
  age:{type:Number,default:0}
})

module.exports = model('User', userSchema)

实现增删改的逻辑层

/controllers/users.js

const User = require('../models/user');

class UsersCtl {
  async find(ctx) {
    ctx.body = await User.find();
  }

  async findById(ctx) {
    const user = await User.findById(ctx.params.id);
    if(!user) {ctx.throw(404,'用户不存在')};
    ctx.body = user
  }

  async create(ctx) {
    const user = await new User(ctx.request.body).save();
    ctx.body = user;
  }

  async update(ctx) {
    const user = await User.findByIdAndUpdate(ctx.params.id, ctx.request.body);
    const users = await User.findById(ctx.params.id);
    if (!users) { ctx.throw(404, '用户不存在'); }
    ctx.body = users;
  }
  async delete(ctx) {
    const user = await User.findByIdAndRemove(ctx.params.id);
    if (!user) { ctx.throw(404, '用户不存在'); }
    ctx.status = 204;
  }
}

module.exports = new UsersCtl();

子路由users.js

const Router = require('koa-router');
//const router = new Router({ prefix: '/users' });
const { find, findById, create,
  update, delete: del, } = require('../controllers/users');

router.get('/', find);
router.post('/', create);
router.get('/:id', findById);
router.patch('/:id', update);
router.delete('/:id', del);


module.exports = router;

8、多个子路由模块加载,如何优化?

/routes/index.js

const fs  = require('fs');

module.exports = (app) => {
  fs.readdirSync(__dirname).forEach(file =>{
    if(file === 'index.js') return;
    console.log(file,11111)
    const route = require(`./${file}`)
    app.use(route.routes()).use(route.allowedMethods());
  })
}

/routes/users.js

const Router = require('koa-router')
const router = new Router({prefix:'/api/users'});
const { find, findById, create, update, delete: del, } = require('../controllers/users');

router.get('/', find);
router.post('/', create);
router.get('/:id', findById);
router.patch('/:id', update);
router.delete('/:id', del);

module.exports = router;

/routes/product.js

const Router = require('koa-router');
const router = new Router({prefix:'/api/product'});

router.get('/',async ctx =>{
  ctx.body = 'project'
})

module.exports = router;

app.js

const Koa = require('koa');
const koaBody = require('koa-body');
const mongoose = require('mongoose');
const routing = require('./routes/index')
const { connectionStr } = require('./config.js')
// 连接数据库
mongoose.connect(connectionStr,(err)=>{
  if(err) {console.log('mongonDB连接失败了'); return}
  console.log('mongonDB连接成功了');
})

//koa实例化
const app = new Koa();

app.use(koaBody());
routing(app);

app.listen(3000,()=>{
  console.log('服务启动了')
})

9、让我们的接口更加完整吧。

9.1、创建app.js

const Koa = require('koa');
const Router = require('koa-router');
const mongoose = require('mongoose');

const db = require('./config/keys').mongoURL

//连接接数据库
mongoose.connect(db,{ useNewUrlParser: true })
  .then(() =>{
    console.log('mongodb数据库连接成功')
  }).catch(err =>{
    console.log('连接错误'+err)
  })


//实例化koa
const app = new Koa();
const router = new Router();

router.get('/',ctx =>{
  ctx.body = 'hello world'
})

app.use(router.routes()).use(router.allowedMethods());

let port = process.env.port || 5000;

app.listen(port,()=>{
  console.log(`正在监听端口号${port}的服务`)
})

9.2、./config/keys.js

连接数据库的字符串

module.exports = {
mongoURL:'mongodb+srv://tanwei:tanwei1010@cluster0.ooyzv.mongodb.net/myFirstDatabase?retryWrites=true&w=majority'
}

9.3、创建用户模型和测试接口

const Router = require('koa-router');
const router = new Router();


/**
 * @route GET api/users/test
 * @desc 测试接口地址
 * @access 接口是公开的
 */
router.get('/test',async ctx =>{
  ctx.status = 200
  ctx.body = {msg:'test ....'}
})

module.exports = router.routes();

9.4、引入子路由

const Koa = require('koa');
const Router = require('koa-router');
const mongoose = require('mongoose');
const users = require('./routes/users');

const db = require('./config/keys').mongoURL

//连接接数据库
mongoose.connect(db,{ useNewUrlParser: true })
  .then(() =>{
    console.log('mongodb数据库连接成功')
  }).catch(err =>{
    console.log('连接错误'+err)
  })


//实例化koa
const app = new Koa();
const router = new Router({{prefix:'/api'});

router.get('/',ctx =>{
  ctx.body = 'hello world'
})

// localhost:8080/api/users/test
router.use('/users',users);
app.use(router.routes()).use(router.allowedMethods());

let port = process.env.port || 5000;

app.listen(port,()=>{
  console.log(`正在监听端口号${port}的服务`)
})

9.5、创建一个用户模型

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

//实例化一个数据模板
const UserSchema = new Schema({
  name:{type:String,required:true},
  email:{type:String,required:true},
  password:{type:String,required:true},
  date:{type:Date,default:Date.now}
})

module.exports = mongoose.model('Users',UserSchema);

9.6、注册接口

const Router = require('koa-router');
const router = new Router();

//引入user
const User = require('../models/User')

/**
 * @route GET api/users/test
 * @desc 测试接口地址
 * @access 接口是公开的
 */
router.get('/test',async ctx =>{
  ctx.status = 200
  ctx.body = {msg:'test ....'}
})

/**
 * @route GET api/users/register
 * @desc 测试接口地址
 * @access 接口是公开的
 */
router.post('/register',async ctx =>{
  console.log(ctx.request.body);
  ctx.body = ctx.request.body;

  //存储到数据库
   const findResult = await User.find({email:ctx.request.body.email});
   if(findResult.length>0){
    ctx.body = {msg:'邮箱已经存在!!!'}
   } else {
     //没查到
     const newUser = new User({
       name:ctx.request.body.name,
       email:ctx.request.body.email,
       password:ctx.request.body.password
     })

     //存储到数据库
    await newUser.save().then(res =>{
      ctx.body = newUser
    }).catch(err =>{
      console.log(err)
    })
   }

})


module.exports = router.routes();

9.7、从路由模块user.js的注册接口,抽取controllers层

const Router = require('koa-router');
const router = new Router();

const { registerUser, login } = require('../controllers/userControllers.js')

/**
 * @route GET /api/users/test
 * @desc 测试接口地址
 * @access 接口是公开的
 */
router.get('/test',async ctx =>{
  ctx.status = 200
  ctx.body = {msg:'test ....'}
})

/**
 * @route GET api/users/register
 * @desc 测试接口地址
 * @access 接口是公开的
 */
 router.post('/register',registerUser)

module.exports = router.routes();

9.8、controllers层代码/controllers/userControllers.js

//引入user
const User = require('../models/Users')

class Users {
  // 注册接口
  async registerUser(ctx){
    // console.log(ctx.request.body);
   ctx.body = ctx.request.body;
   //存储到数据库
    const findResult = await User.find({email:ctx.request.body.email});
    // findResult数组
    if(findResult.length>0){
     ctx.body = {msg:'邮箱已经存在!!!'}
    } else {
      //没查到
      const newUser = new User({
        name:ctx.request.body.name,
        email:ctx.request.body.email,
        password:encryptPW(ctx.request.body.password)
      })
 
      //存储到数据库
     await newUser.save().then(res =>{
       ctx.body = newUser
     }).catch(err =>{
       console.log(err)
     })
    }
  }
}


module.exports = new Users()

10、登录接口

/routers/user.js

 /**
 * @route POST api/users/login
 * @desc 登录接口地址
 * @access 接口是公开的
 */
  router.post('/login',login)

/controllers/userControllers.js

//引入user
const User = require('../models/Users')
const { encryptPW, isRightPW } = require('../config/utils.js')
const { secretKEY } = require('../config/key.js')
const jwt = require('jsonwebtoken');

class Users {
  // 注册接口
  async registerUser(ctx){
    // console.log(ctx.request.body);
   ctx.body = ctx.request.body;
   //存储到数据库
    const findResult = await User.find({email:ctx.request.body.email});
    // findResult数组
    if(findResult.length>0){
     ctx.body = {msg:'邮箱已经存在!!!'}
    } else {
      //没查到
      const newUser = new User({
        name:ctx.request.body.name,
        email:ctx.request.body.email,
        password:encryptPW(ctx.request.body.password)
      })
 
      //存储到数据库
     await newUser.save().then(res =>{
       ctx.body = newUser
     }).catch(err =>{
       console.log(err)
     })
    }
  }

  async login(ctx){
    const findResult = await User.find({email:ctx.request.body.email});

    if(findResult.length === 0){
      ctx.status = 404
      ctx.body= {email:'用户不存在!!!'}
    } else {
      const user = findResult[0];
      const password = ctx.request.body.password;
      // 返回true和false
      const result = isRightPW(password, user.password);
      if(result){
         // 返回token
        const userInfo = {id:user.id,name:user.name};
        //生成token
        const token = jwt.sign(userInfo,secretKEY,{expiresIn:'1h'})
        ctx.status = 200;
        ctx.body = {msg:'登录成功!!!',token:'Bearer '+token};
      } else {
        ctx.status = 400;
        ctx.body = {msg:'登录失败!!!'};
      }
  
    }
  }
}


module.exports = new Users()

./middleware/userMiddleware.js

//验证token的中间件
const jwt = require('jsonwebtoken');
const secretKEY = require('../config/key').secretKEY;

const auth = async (ctx, next) =>{
  const { authorization = '' } = ctx.request.header;
  const token = authorization.replace('Bearer ','');
  try {
    const user = jwt.verify(token,secretKEY)
    ctx.state.user = user
  } catch (error) {
    ctx.throw(401,'请登录!!!')
  }
  await next()
}
module.exports = {
  auth
}

密码加密,加密后的密码比对

/config/utils.js

const bcrypt = require('bcryptjs');
//加密
const encryptPW = (password) =>{
  const salt = bcrypt.genSaltSync(10);
  const hash = bcrypt.hashSync(password, salt);
  return hash;
}
//比对加密后的密码
const isRightPW = (password,mgPassword) => {
  return bcrypt.compareSync(password, mgPassword)
}


module.exports = {
  encryptPW,
  isRightPW
}

11、koa-body获取上传文件

1、安装

koa-body npm install koa-body -S

2、设置图片上传目录

3、使用postman上传文件

//我们利用koa-body来接收所有上传的文件 26行
//新建了一个home模块来写图片上传的接口
const path = require('path');
const Koa = require('koa');
const Router = require('koa-router');
const koaBody = require('koa-body');
const mongoose = require('mongoose');
const user = require('./routers/user.js');
const home = require('./routers/home.js');


const app = new Koa();
const router = new Router({prefix:'/api'});

const { mongoURL } = require('./config/key')
mongoose.connect(mongoURL,(err)=>{
  if(err) {console.log('mongonDB连接失败了'); return}
  console.log('mongonDB连接成功了');
})


router.get('/', async ctx =>{
  ctx.body = "首页"
})

app.use(koaBody({
  multipart:true,
  formidable:{
    uploadDir:path.join(__dirname,'/public/uploads'),
    keepExtensions:true
  }
}));
router.use('/users',user);
router.use('/home',home);
app.use(router.routes()).use(router.allowedMethods());

app.listen(3000,()=>{
  console.log(`我正在监听3000端口号`);
})

/routers/home.js

const Router = require('koa-router');
const router = new Router();
const { upload } = require('../controllers/homeControllers.js')

/**
 * @route GET /api/users/test
 * @desc 测试接口地址
 * @access 接口是公开的
 */
router.get('/test',async ctx =>{
  ctx.status = 200
  ctx.body = {msg:'test ....'}
})

/**
 * @route GET api/users/register
 * @desc 测试接口地址
 * @access 接口是公开的
 */
 router.post('/upload',upload)

  
module.exports = router.routes();

/controllers/homeControllers.js

class Home {
  async upload(ctx){
    const file = ctx.request.files.file;
    ctx.body = {path:file.path}
  }
}

module.exports = new Home()

12、koa-static生成图片链接

1、安装

koa-static npm install koa-static -S

2、设置静态文件目录

3、生成图片链接

const KoaStatic = require('koa-static')

app.use(KoaStatic(path.join(__dirname,'public')))
app.use(koaBody({
  multipart:true,
  formidable:{
    uploadDir:path.join(__dirname,'/public/uploads'),
    keepExtensions:true
  }
}));
router.use('/users',user);
router.use('/home',home);
app.use(router.routes()).use(router.allowedMethods());

/controllers/homeControllers.js

const path = require('path');

class Home {
  async upload(ctx){
    const file = ctx.request.files.file;
    const basename = path.basename(file.path);
    ctx.body = {url:`${ctx.origin}/uploads/${basename}`}
  }
}

module.exports = new Home()

13、删除用户接口

/routers/user.js

/**
 * @route POST api/users/deleteUser
 * @desc 删除一个用户接口地址
 * @access 接口是公开的
 */
 router.post('/deleteUser', auth, delList);

/controllers/userControllers.js

async delList(ctx) {
    const findResult = await Lists.findOne({
      _id: ctx.request.body.id,
    });
    if (!findResult) {
      ctx.body = { code: 500, message: "用户不存在" };
    } else {
      const result = await Lists.remove({ _id: ctx.request.body.id });
      if (result) {
        ctx.body = { message: "删除成功!!!" };
      } else {
        ctx.throw(500, "服务器错误");
      }
    }
}

14、用户列表-分页

/controllers/userControllers.js

//用户列表-分页
async getList(ctx) {
    let { page = 1, limit = 3 } = ctx.query;
    const findList = await Lists.find();
    const userList = await Lists.find().skip((page - 1) * limit).limit(limit * 1);
    ctx.body = {
      cede: 200,
      message: "获取成功",
      data: userList,
      count: findList.length,
    };
}

15、用户列表-模糊搜索

//用户列表-模糊查询
async searchList(ctx) {
    let { page = 1, limit = 3, shopName = "" } = ctx.query;

    let shopNameRegExp = new RegExp(shopName);
    const userList = await Lists.find({ shopName: shopNameRegExp })
      .skip((page - 1) * limit)
      .limit(limit * 1);
    ctx.body = { message: "查询成功", data: userList };
}