koa+mongo+mongoose基础操作

一、初始化项目

npm init //生成package.json
npm i --save koa //安装koa
npm i

二、hello world

创建index.js:

const Koa = require('koa');
const app = new Koa();

app.use((ctx) => {

  ctx.body = 'Hello World1122';
})

app.listen(3000);

三、修改后自动重启

安装nodemon

npm i --save nodemon

启动:

nodemon index.js

四、koa-router

安装:

npm i --save koa-router
const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();

// koa-router
router.get('/', (ctx) => {
  ctx.body = '这是主页';
})

//将中间件插入koa实例
app.use(router.routes()); 

app.listen(3000);

路由前缀:

const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// 路由前缀
const usersRouter = new Router({prefix: '/users'});

// koa-router
router.get('/', (ctx) => {
  ctx.body = '这是主页';
})
usersRouter.get('/', (ctx) => {
  ctx.body = '这是用户';
})
usersRouter.get('/:id', (ctx) => {
  ctx.body = `这是用户 ${ctx.params.id}`;
})

//将中间件插入koa实例
app.use(router.routes()); 
app.use(usersRouter.routes()); 

app.listen(3000);

 

多中间件:

const Koa = require('koa');
const Router = require('koa-router');
const app = new Koa();
const router = new Router();
// 路由前缀
const usersRouter = new Router({prefix: '/users'});

// 多中间件
const auth = async(ctx, next) => {
  if(ctx.url !== '/users') {
    ctx.throw(401);
  }
  await next();
}

// koa-router
router.get('/',auth, (ctx) => {
  ctx.body = '这是主页';
})
usersRouter.get('/',auth, (ctx) => {
  ctx.body = '这是用户';
})
usersRouter.get('/:id',auth, (ctx) => {
  ctx.body = `这是用户 ${ctx.params.id}`;
})

//将中间件插入koa实例
app.use(router.routes()); 
app.use(usersRouter.routes()); 

app.listen(3000);

 

 

五、获取http请求参数

参数形式:(用url传递参数有长度限制和敏感信息不安全等缺点)

1.查询字符串:?q=keyword 

   获取:ctx.query

2.路由参数:/users/:id (一般是必选的)

   获取:ctx.params

3.请求体:{name: '李磊'} (两种形式:json和form,json:application/json, form:application/x-www-form-urlencoded)

   获取:ctx.request.body(需安装中间件koa-bodyparser)

 

六、HTTP响应

1.发送status: ctx.status

2.发送body: ctx.body

3.发送header: ctx.set('Allow','GET,POST');

 

七、合理分化路由和控制器

将上方项目中的路由和控制器放到单独的文件中:

创建app文件夹:创建routes文件夹和controllers文件夹,分别存放路由和控制器

controllers:

home.js:

class HomeCtl {
    index(ctx) {
        ctx.body = '这是首页1';
    }
}
module.exports = new HomeCtl();

user.js:

class UserCtl {
    find(ctx) {
        ctx.body = '获取用户列表';
    }
    findById(ctx) {
        ctx.body = '获取指定用户';
    }
    create(ctx) {
        ctx.body = '新建用户';
    }
    update(ctx) {
        ctx.body = '修改指定用户';
    }
    delete(ctx) {
        ctx.body = '删除指定用户';
    }
}

module.exports = new UserCtl();

 

routes:

home.js:

const Router = require('koa-router');
const router = new Router();
const { index } = require('../controllers/home')

// router.get('/', (ctx) => {
//     ctx.body = '首页';
// })
router.get('/', index);
module.exports = router;

user.js:

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

router.get('/', find)
// 获取用户
router.get('/:id', findById)
// 新建用户
router.put('/:id', create)
// 修改用户
router.put('/:id', update)
// 删除用户
router.delete('/:id', del);

module.exports = router;

index.js:

// 此文件功能:在app中批量注册路由
const fs = require('fs');
module.exports = (app) => {
    fs.readdirSync(__dirname).forEach(file => {
        if(file === 'index.js') { return; }
        const route = require(`./${file}`);
        app.use(route.routes()).use(route.allowedMethods());
    })
}

 

app/index.js:

const Koa = require('koa');
const bodyparser = require('koa-bodyparser');
const app = new Koa();
const routing = require('./routes');

app.use(bodyparser()); //解析请求体

// app中注册路由
// app.use(router.routes());
// app.use(userRouter.routes());
// app.use(userRouter.allowedMethods()); //允许使用options获取请求方式
routing(app);

app.listen(3333, () => {console.log('程序启动')});

 

八、错误处理

1.状态码

(1)运行时错误:500

(2)逻辑错误:找不到(404)、先决条件失败(412)、无法处理的实体(参数格式不对422)等

2.koa自带的错误处理

(1)404

(2)412:ctx.throw(412, '先决条件失败:id大于等于数组长度');

(3)500

3.自定义koa错误处理中间件

const Koa = require('koa');
const bodyparser = require('koa-bodyparser');
const app = new Koa();
const routing = require('./routes');

// 自定义错误处理中间件(需写在所有中间件前)
app.use(async(ctx, next) => {
    try {
        await next();
    } catch(err) {
        ctx.status = err.status || err.statusCode || 500;
        ctx.body = {
            message: err.message
        }
    }
})

app.use(bodyparser()); //解析请求体

// app中注册路由
// app.use(router.routes());
// app.use(userRouter.routes());
// app.use(userRouter.allowedMethods()); //允许使用options获取请求方式
routing(app);

app.listen(3333, () => {console.log('程序启动')});

4.用koa-json-error进行错误处理

安装:npm i --save koa-json-error

在app/index.js中引入,写在所有中间件前:

const error = require('koa-json-error');
app.use(error());

 

在生产环境中不应该出现信息过多的stack字段,则分别设置分生产和开发环境的报错返回值:

// koa-json-error处理错误
app.use(error({
    // 设置报错信息中的stack不在生产环境返回,只在测试环境返回
    postFormat: (e, {stack, ...rest}) => process.env.NODE_ENV == 'production' ? rest : {stack, ...rest}
}));

测试:

安装跨平台设置环境变量插件: cross-env.  npm i --save -dev cross-env

在package.json中设置环境变量:

{
  ......
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "cross-env NODE_ENV=production nodemon app",
    "dev": "cross-env NODE_ENV=development nodemon app"
  },
  ......
}

 

九、校验请求参数(koa-parameter)

安装koa-parameter: cnpm i --save koa-parameter 

 在app/index.js中引入koa-parameter:

const parameter = require('koa-parameter');
// 校验请求参数
app.use(parameter(app));

在app/controllers/users.js中使用,校验传入的参数:

create(ctx) {
        ctx.body = '123';
        // 使用koa-parameter校验
        ctx.verifyParams({
            name: {type: 'string', require: true}
        })
}

 

 十、数据库(MongoDB Atlas)

1.注册并创建集群:https://cloud.mongodb.com

按提示步骤创建:

2.安装mongoose:cnpm i --save mongoose

创建文件夹存储数据库链接:app/config.js:

module.exports = {
    connectionStr: 'mongodb+srv://jixueyuandi:[email protected]/test?retryWrites=true&w=majority'
}

// 真正开发时,要用环境变量等方式获取密码,不要把密码写在代码里

使用mongoose连接云数据库,app/index.js:

const {connectionStr} = require('./config.js');
// 连接mongoose数据库
const mongoose = require('mongoose');
mongoose.connect(connectionStr, () => console.log('数据库连接成功'));
mongoose.connection.on('error', console.error);

3.实现用户的增删改:

(1)创建用户模式及模型

app中新建models/users.js:

// 此文件用来创建用户模型
const mongoose = require('mongoose');
const {Schema, model} = mongoose;

const userSchema = new Schema({
    name: {type:String,require:true}
});

module.exports = model('User',userSchema); //model(集合名,模式实例)

 

(2)app中新建controllers/home.js、user.js

home.js:

class HomeCtl {
    index(ctx) {
        ctx.body = '这是首页1';
    }
}
module.exports = new HomeCtl();

 

user.js:

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

class UserCtl {
    async find(ctx) {
        ctx.body = await User.find();
    }
    async findById(ctx) {
        ctx.body = await User.findById(ctx.params.id);
    }
    async create(ctx) {
        // 使用koa-parameter校验
        ctx.verifyParams({
            name: {type: 'string', require: true},
            password: {type: 'string', require: true}
        })
        // 用户名不重复使用
        const {name} = ctx.request.body;
        const repeatedUser = await User.findOne({name});
        if (repeatedUser) {
            ctx.throw(409, '用户名已被占用');
        }
        const user = await new User(ctx.request.body).save();
        ctx.body = user;
    }
    async update(ctx) {
        ctx.verifyParams({
            name: {
                type: 'string',
                require: false
            },
            password: {type: 'string', require: false}
        })
        const user = await User.findByIdAndUpdate(ctx.params.id, ctx.request.body);
        if(!user) {
            ctx.throw(404, '用户不存在')
        }
        ctx.body = user;
    }
    async delete(ctx) {
        const user = await User.findByIdAndRemove(ctx.params.id);
        if (!user) {
            ctx.throw(404, '用户不存在')
        }
        ctx.status = 204;
    }
}

module.exports = new UserCtl();

 

(3)app中新建routes/home.js、user.js、index.js

home.js:

const Router = require('koa-router');
const router = new Router();
const { index } = require('../controllers/home')

// router.get('/', (ctx) => {
//     ctx.body = '首页';
// })
router.get('/', index);
module.exports = router;

home.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.get('/:id', findById)
// 新建用户
router.post('/', create)
// 修改用户
router.patch('/:id', update)
// 删除用户
router.delete('/:id', del);

module.exports = router;

 

index.js:

// 此文件功能:在app中批量注册路由
const fs = require('fs');
module.exports = (app) => {
    fs.readdirSync(__dirname).forEach(file => {
        if(file === 'index.js') { return; }
        const route = require(`./${file}`);
        app.use(route.routes()).use(route.allowedMethods());
    })
}

 

十一、jwt实现登录认证

1.jwt实现过程:在用户登录时实行jwt签名,返回token到客户端,客户端将token存储到sessionStorage或localStorage中,在需要用户验证的接口请求时实行jwt验证

2. 安装jsonwebtoken和koa-jwt

cnpm i --save jsonwebtoken
cnpm i --save koa-jwt

3.实现:

在/config.js中设置签名(真正开发时不能在这设置,应该在不同环境变量中获取):

module.exports = {
    secret: 'zhihu-jwt-secret',
    connectionStr: 'mongodb://127.0.0.1:27017/myuser' //'mongodb+srv://jixueyuandi:[email protected]/test?retryWrites=true&w=majority'
}

// 真正开发时,要用环境变量等方式获取密码,不要把密码写在代码里

 

在/app/controllers/user.js中写登录接口:

const User = require('../models/users');
const jsonwebtoken = require('jsonwebtoken');
const {secret} = require('../config.js');

class UserCtl {
......
    // 验证是否是该用户
    async checkOwner(ctx, next) {
        console.log(ctx.params)
        if(ctx.params.id !== ctx.state.user._id) {
            ctx.throw(403, '没有权限');
        }
        await next();
    }

    //登录控制器
    async login(ctx) {
        ctx.verifyParams({
            name: {type: 'string', required: true},
            password: {type: 'string', required: true}
        })
        const user = await User.findOne(ctx.request.body);
        if(!user) {
            ctx.throw(401, '用户名或密码不正确')
        }
        const {_id, name} = user;
        const token = jsonwebtoken.sign({_id,name}, secret, {expiresIn: '1d'})
        ctx.body = token;
    }
}
module.exports = new UserCtl();

 

在/app/routes/user.js中设置登录接口 并对需认证的接口添加认证控制器和验证是否是该用户控制器:

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

// const jsonwebtoken = require('jsonwebtoken');
const {secret} = require('../config')
const jwt = require('koa-jwt');

// 方法一:用koa-jwt创建token认证中间件
const auth = jwt({secret:secret})

// 方法二:用jsonwebtoken创建token认证中间件
// const auth = async (ctx, next) => {
//     // 获取客户端header传递过来的authorization中的token
//     const {authorization=''} = ctx.request.header;
//     const token = authorization.replace('Bearer ', '');
//     // 进行token验证
//     try {
//         const user = jsonwebtoken.verify(token, sercet);
//         ctx.state.user = user;
//         console.log(ctx.state)

//     } catch(err) {
//         ctx.throw(401, err.message);
//     }
//     await next();
// }

router.get('/', find)
// 获取用户
router.get('/:id', findById)
// 新建用户
router.post('/', create)
// 修改用户
router.patch('/:id', auth, checkOwner, update)
// 删除用户
router.delete('/:id', auth, checkOwner, del);

// 用户登录
router.post('/login', login);

module.exports = router;

 

十二、上传图片

安装;koa-body

 cnpm i --save koa-body

在app/index.js中引入:

......
const path = require('path');
const koaBody = require('koa-body');
app.use(koaBody({
    multipart: true, //上传文件
    formidable: {
        uploadDir: path.join(__dirname, 'public/uploads'), //上传位置
        keepExtensions: true //保留原后缀名
    }
});

在controllers/home.js中引入:

const path = require('path');
class Home{
    ....
    upload(ctx) {
        const file = ctx.request.files.file;
        //ctx.body = {path: file.path}; //返回上传的本地文件路径
        //返回链接路径
        const basename = path.basename(file.path);
        ctx.body = {url:`${ctx.origin}/uploads/${basename}`};
    }
}
module.sxports = new Home();

在router/home.js中写相对应接口:

const Router = require('koa-router');
const router = new Router();
const { index,upload } = require('../controllers/home')

// router.get('/', (ctx) => {
//     ctx.body = '首页';
// })
router.get('/', index);
router.post('/upload',upload)
module.exports = router;

 

将文件转为链接:

安装koa-static

在app/index.js中引入并使用:

const koaStatic = require('koa-static');
app.use(koaStatic(path.join(__dirname, 'public'});

图片路径:http://127.0.0.1:3333/uploads/upload_926df733d5384256a5446f95eee64858.png

 

html中提交图片:在public/uploads中创建upload.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset='utf-8'>
  <meta http-equiv='X-UA-Compatible' content='IE=edge'>
  <title>Page Title</title>
  <meta name='viewport' content='width=device-width, initial-scale=1'>
</head>
<body>
  <form enctype="multipart/form-data" action="/upload" method="POST">
    <input type="file" name="file" accept="image/*">
    <button type="submit">提交</button>
  </form>
</body>
</html>

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章