上节回顾
-
Element
、scss
、axios
的使用 - 路由定义与跳转
- 登录&注册客户端逻辑
工作内容
-
crypto-js
加密 - 服务端参数校验
准备工作
-
npm i -S crypto-js
// 先切换到/client
目录下 -
npm install -S koa-bouncer
// 先切换到/server
目录下
更新逻辑
前端加密
上几节保存的密码都是明文的,这里借助crypto-js
加密密码。
// 更新文:client/src/views/login/index.vue
...
import md5 from 'crypto-js/md5'
...
const res = await http.post(
'/users?action=login',
{
account,
password: md5(password).toString()
}
)
...
const res = await http.post(
'/users?action=register',
{
account,
password: md5(password).toString()
}
)
...
后端校验
在前端表单提交中,有校验逻辑:
可是,通过Postman
/其它方式请求接口时,不会走这些校验,所以,后端校验是很必要的。
不借助库校验
不借助第三方包的话,就自定义一个中间件(这里举特例)
// 新建文件:server/validation/user.js
const validate = (ctx, next) => {
const { account, password } = ctx.request.body;
// account: [
// { required: true, message: '请输入帐号', trigger: 'blur' },
// { min: 5, message: '长度至少5个字符', trigger: 'blur' }
// ],
// password: [
// { required: true, message: '请输入密码', trigger: 'blur' },
// { min: 3, message: '长度至少3个字符', trigger: 'blur' }
// ]
if (!account || !password) {
ctx.body = {
code: '403',
data: null,
msg: '帐号/密码不能为空'
}
return;
}
if (account.trim().length < 5) {
ctx.body = {
code: '403',
data: null,
msg: '帐号长度至少5个字符'
}
return;
}
next()
}
module.exports = {
validate
}
// 更新文件:server/router/users.js
...
const { validate } = require('../validation/user'); //新增
...
{
path: '/',
method: 'POST',
handle: async (ctx) => {
...
},
middlewares: [
validate //新增
]
},
...
//更新文件:server/utils/router.js
function register(routes) {
routes.forEach((route, idx) => {
const { path, method, middlewares = [], handle } = route; // 加入middlewares
this[method.toLowerCase()](path, ...middlewares, async (ctx, next) => {
await handle(ctx);
})
})
}
module.exports = {
register,
}
使用koa-bouncer
校验
和‘不借助库校验’二选一,使用该方法时,注意还原‘不借助库校验’的代码。
// 更新文件:server/app.js
...
const bouncer = require('koa-bouncer'); //新增
const routes = require('./router');
const app = new koa();
app.use(cors());
app.use(bouncer.middleware()); //新增
// 中间件的错误处理
app.use(function(ctx, next){//新增
console.log(err)
if (err.name === 'ValidationError') { //bouncer校验失败会抛出错误,根据错误类型作出相应处理
ctx.body = {
code: '403',
data: null,
msg: err.message
}
}
});
});
...
bouncer校验失败会抛出错误,根据错误类型作出相应处理
// 更新文件:server/router/users.js
...
const validate = async (ctx, next) => {
ctx.validateBody('account')
.required('帐号必填')
.isString('帐号必须是字符串')
.trim()
.tap(x => x.length)
.gte(4, '帐号长度至少为5个字符')
ctx.validateBody('password')
.required('密码必填')
.isString('密码必须是字符串')
.trim()
await next(); // 这里必须是async/await形式,否则,如动态图,没有返回值。
}
...
{
path: '/',
method: 'POST',
handle: async (ctx) => {
const { action } = ctx.query;
switch (action) {
case 'register':
await register(ctx);
break;
case 'login':
await login(ctx);
break;
default:
await list(ctx);
}
},
middlewares: [
validate // 新增
]
},
...
//更新文件:server/utils/router.js
function register(routes) {
routes.forEach((route, idx) => {
const { path, method, middlewares = [], handle } = route; // 加入middlewares
this[method.toLowerCase()](path, ...middlewares, async (ctx, next) => {
await handle(ctx);
})
})
}
module.exports = {
register,
}