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 ' }else{ throw err; } }) }) // 借用koa中间件对token进行验证 // token 验证失败的时候会抛出401错误,因此需要添加错误处理,而且要放在 app.use(koajwt()) 之前,否则不执行。 app.use(koaJwt({ secret: keys.secretkey }).unless({ path:['/user/loginUser','/user/logoutUser'] }))
但是注意,上面的方法并没有区分token的过期和无效,具体可以查看 ode_moduleskoa-jwtlib esolversauth-header.js 和 koa-jwtlibindex.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