koa mongoose 实践篇,各种必要的功能总结;
https://www.cnblogs.com/xhliang/p/11913119.html 记录了一些理论知识,但是在实际开发过程中还存在很多疑惑,下面列举几个常用功能点;
1 倒叙,分页,总条数,模糊查询
router.get('/getList',async (ctx)=>{
const List = mongoose.model('List')
// 创建一个查询条件 利用$and 由于$and不能为空数组,所以初始化时设置了个空对象
const queryCriteria = {$and:[{}]}
const { myId ,title, type, currentPage, pageSize} = ctx.query
if(myId){
queryCriteria.$and.push({myId:myId})
}
if(type){
queryCriteria.$and.push({type:type})
}
if(title){
// title 支持模糊查询
const regTitle = new RegExp(title,'i')
queryCriteria.$and.push({title:{$regex:regTitle}})
}
// 查询总条数
const total = await List.countDocuments(queryCriteria)
// 根据条件进行分页查询,limit控制返回的最大条数,skip控制第几页,sort用于排序(1正叙,-1倒叙)
await List.find(queryCriteria).sort({time:-1}).limit(parseInt(pageSize)).skip((parseInt(currentPage)-1)*parseInt(pageSize)).then((res)=>{
ctx.body = {
code:200,
data:{
list:res,
total:total
}
}
})
})
2 关联查询,比如一个表中有aId bId id aId和bId都关联另外一个表,我想查询aId关联的表后再查询当前表之后返回内容;(没有找到更好的办法,如果有大神路过请指点)
实现法法:先根据条件进行关联查询,后再按条件查询都成功后返回给客户端数据;
3 字段必填的设定,新建利用required: true
const listSchema = new Schema({
id:Schema.Types.ObjectId,
name:{ type:String, required: true })
更新和删除数据时,非新建有的字段 如自动生成的id需要手动去验证,而新建时就required的字段,只需要设置运行验证器既可(详见第五点);(不知道有没有更好的办法,欢迎路过的给出较好的答案)
router.put('/updateList',async ctx=>{
const List= mongoose.model('List')
const updateId = ctx.request.body._id
// 更新时id必选
if(!updateId){
ctx.body={
code:405,
message:"Id was not found"
}
}else{
await List.update({_id:updateId},{$set:ctx.request.body}).then(res=>{
ctx.body = {
code:200,
message:'Update success'
}
}).catch(err=>{
console.log(err)
ctx.body={
code:500,
message:err
}
})
}
})
4 字段唯一性,不能被重复;
字段唯一性的控制是在new Schema时 设置字段的属性{unique:true}
const listSchema = new Schema({
id:Schema.Types.ObjectId,
name:{ type:String,unique:true },
type:String,
desc:String,
aId:String,
bId:String,
time: { type: Date, default: Date.now() },
})
5 字段更新update时如何保证必填、唯一性和字段类型等也被验证
在官方文档中介绍了很多字段,其中runValidators:如果为真,则在此命令上运行更新验证器。更新验证器根据模型的模式验证更新操作,这样就保证了数据的准确性;
List.update({_id:updateId},{$set:ctx.request.body},{runValidators:true})
还有一个字段 upsert (boolean) 如果文档不匹配,是否创建文档(false),一般更新的时候 不需要去创建文档 ,只要更新原来的内容既可可以设置为fasle,默认也是fasle
6 字段可能为Array类型, 也可以为String类型么?
可以利用mixed来实现:这样mykey就可以传入各种类型
const listSchema = new Schema({mykey:Schema.Types.Mixed})
7 对error的封装
针对数据库报出来的错误基本都是数据错误,想必填的未传递,唯一性的传递重复的值,类型与设定的不一致等等;
有两种方式,自己写方法去验证和封装数据库报出来的错误传递给客户端;
但是封装错误时发现mongodb的error没有太多规律,也没有专们的文档,一般返回的是res.message;封装数据库报出来的错误就会增加数据库的压力,若读写操作特别频繁的话建议自己封装方法;
8 使用jwt进行token鉴权 (需要先安装jwt 和中间件koa-jwt)
A- 服务端利用jwt生成token
//登录时,匹配成功后生成token并返回给客户端
const jwt = require('jsonwebtoken');
const keys = require('./../config/key.js')
const token = jwt.sign(payload,keys.secretkey,{expiresIn:3600})
ctx.body = {
code: 200,
message: '登录成功',
data: {
userName:result.userName,
token:token
}
};
B- web端,登录成功保存token,并在拦截器中统一添加token,
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token');
config.headers.common['Authorization'] = 'Bearer ' + token;
return config;
})
C- 服务端统一进行拦截对token进行验证,主要这个拦截器一定位于各个接口路由之前,否则不执行
const koaJwt = require('koa-jwt')
const keys = require('./config/key.js')
// token验证拦截器
app.use( function(ctx, next) {
// console.log('执行了拦截器',ctx,next)
return next().catch(err=>{
console.log('tokenerr',err)
if(err.status == 401){
ctx.status = 401
ctx.body = 'Protected resource, use Authorization header to get access\n'
}else{
throw err;
}
})
})
// 借用koa中间件对token进行验证
// token 验证失败的时候会抛出401错误,因此需要添加错误处理,而且要放在 app.use(koajwt()) 之前,否则不执行。
app.use(koaJwt({ secret: keys.secretkey }).unless({
path:['/user/loginUser','/user/logoutUser']
}))
但是注意,上面的方法并没有区分token的过期和无效,具体可以查看\node_modules\koa-jwt\lib\resolvers\auth-header.js 和 koa-jwt\lib\index.js,进行了统一处理;

koajwt 封装了verify的方法,也可以自己写方法进行验证,下面伪代码提供一个思路;
let token = req.header.token
jwt.verify(token,keys.secretkey, (err,decoded)=>{
if (err) {
switch (err.name) {
case 'JsonWebTokenError':
res.status(403).send({ code: -1, msg: '无效的token' });
break;
case 'TokenExpiredError':
res.status(403).send({ code: -1, msg: 'token过期' });
break;
}
}
}
jwt验证相关详细参考:https://github.com/lin-xin/blog/issues/28
浙公网安备 33010602011771号