【今日成果】:
//啊哈哈哈 , 莫名其妙入选了。
【快速回顾】:
(1):
虽然提交表单的时候前端做了校验,但是通过PostMAN接口调试,我们发现不规范的数据还是会被存储到数据库中;——需要做后端校验;
(2):
不能在每一个Controller接口上都加上那么一大堆代码,这个时候就需要使用统一异常处理;
(3):
关于code , 随着后面开发的模块越来越多 , 返回的CODE需要区分开来。——这就涉及到编码规则的制定。
(4):
同一个实体类不同的操作对同一个字段的要求是不同的 , 例如ID , 添加的时候不需要校验 ,但是修改的时候就必须得进行非空判断,这个时候就得使用——分组校验;
(5):
自定义校验注解。
(6):
SKU 和 SPU 。
【具体细节】:
【后端校验】:
(1):传入实体对象后,使用if…else…来逐个字段校验;
(2):使用JSR-303 ;
/**
* 保存
*/
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand , BindingResult result){
if (result.hasErrors()){
//提交的数据经过JSR303后 , 有非法的字段。
Map<String,String> map = new HashMap<>();
List<FieldError> fieldErrors = result.getFieldErrors();
for ( FieldError fieldError : fieldErrors ) {
//获取提交的不合法数据的field
String field = fieldError.getField();
//获取非法的field提示信息
String defaultMessage = fieldError.getDefaultMessage();
map.put( field , defaultMessage );
}
return R.error(400,"提交的品牌表单数据不合法").put("data",map);
}
brandService.save(brand);
return R.ok();
}
【实体类上的注解信息】:
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
@TableId
private Long brandId;
@NotEmpty(message = "品牌的名称不能为空")
private String name;
@URL(message = "logo必须是一个合法的URL地址")
@NotEmpty(message = "LOGO不能为空")
private String logo;
@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字母必须是单个的字母")
@NotEmpty(message = "检索首字母不能为空")
private String firstLetter;
@Min(value=0 , message = "排序不能小于0")
@NotNull(message = "排序字段不能为null ")
private Integer sort;
}
【统一异常处理】:
通用的错误列表 , 响应的编码统一为5位数字 , 前面两位约定为业务场景 , 最后三位约定为错误码;
10——表示通用;
/001:参数格式错误 10001
/002:未知异常 10002
11:商品
12:订单
13:物流
14:会员
…
/*
* 错误编码 和 错误信息的枚举类
* */
public enum BizCodeEnume {
UNKNOW_EXCEPTION(10000,"系统未知异常"),
VALID_EXCEPTION(10001,"参数格式异常");
private int code;
private String msg;
BizCodeEnume(int code,String msg){
this.code = code;
this.msg = msg;
}
public int getCode(){
return code;
}
public String getMsg(){
return msg;
}
}
【分组异常处理】:
/**
* 品牌id
*/
@NotNull(message = "更新数据品牌ID必须不为空",groups = {UpdateGroupsInterface.class})
@Null(message = "添加品牌信息品牌ID必须为空",groups = {AddGroupsInterface.class})
@TableId
private Long brandId;
【Controller中的代码】:
【接口】:
【Update】:
【Add】:
【注意】:
一旦指定了分组,那么没有分组的注解就会失效;
如果没有红色区域的注解,只有蓝色的注解 , 那么蓝色的注解会起作用;
但是一旦使用了红色区域的注解,那么———蓝色区域的注解就不会再起作用(因为启用了分组);
【自定义校验注解】:
(1):创建自定义的校验注解;
/**
* 自定义的校验注解
*/
@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {
String message() default "提交的数据必须在数据列表中";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
int[] val() default {};
}
(2):创建一个自定义的校验器;
/**
* 对应的校验注解的校验器
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
private HashSet<Integer> set = new HashSet<>();
/**
* 初始化的方法
* 举例:@ListValue(val={1,0})
* 获取到 1 0
* @param constraintAnnotation
*/
@Override
public void initialize(ListValue constraintAnnotation) {
int[] val = constraintAnnotation.val();// 0 1
for (int i : val) {
set.add(i);
}
}
/**
* 判断校验是否成功的方法
* @param value 客户端传递的对应的属性的值 判断value是否在0 , 1 中
* @param context
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
return set.contains(value);
}
}
(3):关联自定义的校验注解和校验器。