前言
我们这里的防御只谈如何防御后端传的数据本身有问题的情况,举个例子,你和后端约定好,我们的数据格式如下:
{
data: { // data 要求是一个对象
personInfo: { // personInfo 要求是一个对象
name: '张三', // personInfo属性必须有name属性且为字符串
age: 12, // personInfo属性必须有age属性且为数字
},
...XXX //其它属性省略
},
success: 0, // 0 表示成功 1 表示失败
}
复制代码
我们前端如果要用后端数据,我相信绝大部分前端同事都会写这样的代码
// 模拟后端数据
const mockData = {
data: { // data 要求是一个对象
personInfo: { // personInfo 要求是一个对象
name: '张三', // personInfo属性必须有name属性且为字符串
age: 12, // personInfo属性必须有age属性且为数字
},
...XXX //其它属性省略
},
success: 0, // 0 表示成功 1 表示失败
}
复制代码
业务使用
// 用来判断是否是对象
const isObj = (obj) => Object.prototype.toString.call(obj).toLowerCase() == "[object object]";
if(mockData && mockData.data && isObj(mockData.data) && mockData.data.personInfo && isObj(mockData.data.personInfo)){
这里才可以放心的使用mockData.data.personInfo数据
}
复制代码
有些同学可以用es6的?.操作符简化上面的写法,如下:
if(mockData?.data?.personInfo && isObj(mockData.data) && isObj(mockData.data.personInfo)){
这里才可以放心的使用mockData.data.personInfo数据
}
复制代码
看起来简洁了很多,但是我们防御来防御去,各种if判断,真的很烦,因为数据校验,应该在service层做,也就是我们请求数据的fetch库,或者axios的xhr库去解决,我们业务层加入了校验,本身就不符合职责单一原则。
解决思路
那么怎么让service层去做这个事呢,我们可以把每次请求的数据,在service层做一个校验,如果这个数据格式不满足我们和后端商量的格式,那么直接就抛错,不走后面的逻辑了。
也就是说我们要让后端返回的数据百分百满足要求,我们前端才能不做防御性的if判断。(这样完美把锅甩给后端了!重点!重点!重点!)
有人说可以用typescript来做,其实不行,typescript只能在编译阶段起作用,而且打包之后这些类型校验都消失了,咋可以让类型校验在打包后可以生效呢?
class-validator很香
最终我们采取的方案是使用一个叫做class-validator
的库,这个库可以说是一个简化验证的依赖库, 这个必须结合typescript来用,因为要用到装饰器。
用法如下:
import {validate, ValidateNested,Equals, Length, IsNotEmpty, IsNumber} from "class-validator";
// 构造一个用户类型,也就是personInfo的信息
// 这个类既可以作为typescript里的类型校验,又可以给class-validator用
class User {
@Length(10, 20,{message: 'name的长度不能小于10不能大于20'})
@IsNotEmpty({message:'name 不能为空'})
name: string;
@IsNumber({message:'age字段必须是number类型'})
age: number;
}
class Data {
@ValidateNested()
user: User;
constructor() {
this.user = new User();
}
}
class ResponseData {
@ValidateNested()
data: Data;
@Equals(0)
success: number
constructor() {
this.data = new Data();
}
}
let data = {
data: {
personInfo: {
name: '张三',
age: 12,
},
},
success: 0, // 0 表示成功 1 表示失败
}
复制代码
最后验证数据即可
let post = new ResponseData();
validate(post).then(errors => {
if (errors.length > 0) {
console.log(errors);
} else {
console.log("成功啦!");
}
})
复制代码
业务层就当做数据一定的对的去使用,后端数据防御性判断需要单独在一个层面去使用,相信会使代码更简洁,更符合单一原则.