通过商城项目(中)节选-前后端校验及异常处理

前后端校验

前端校验

使用elementUI的组件来进行校验,查看其文档。Form表单->表单验证->自定义校验规则

需求1:“检索首字母”栏内容必须是“字母”

firstLetter: [
            // { required: true, message: '检索首字母不能为空', trigger: 'blur' }
            {
              validator: (rule, value, callback) => {
                if (value === '') {
                  callback(new Error("检索首字母不能为空"))
                }else if(!/^[A-z]$/.test(value)){
                  callback(new Error("必须是字母"))
                }
                callback()
              },
              trigger: 'blur'
            }
          ],

格式参考文档的格式。validator: (rule, value, callback) => {}是elementUI提供的方法

else if( )里面是正则表达式,取反“!”表示不满足该表达式则应该报错。

callback()是回调函数,添加内容,在界面上提示报错信息。

需求2:“排序”栏内容必须是“0到无穷的正整数”

          sort: [
            // { required: true, message: '排序不能为空', trigger: 'blur' }
            {
              validator: (rule, value, callback) => {
                if (value === '') {
                  callback(new Error("排序不能为空"))
                } else if (!/^([1-9]+\d*|0)$/.test(value)) {
                  callback(new Error("必须是正整数或0"))
                }
                callback()
              },
              trigger: 'blur'
            }
          ]

注意格式,和正则表达式的使用。

后端校验

使用JSR303,和Hibernate Validate(前一种的扩展)

@URL(message="回显指定信息") Url校验,不通过则返回提示message。参数下同。

@Pattern(regexp = "^[A-z]$",message = "必须是大小写的英文字母") 自定义匹配。

用于String类型,匹配正则表达式。不能用于Integer等类型。

为什么这儿的正则表达式,又不用/^[A-z]$/斜杠把它们包起来了?反而会出错??

@NotNull

适用于基本数据类型(Integer,Long,Double等等),当 @NotNull 注解被使用在 String 类型的数据上,则表示该数据不能为 Null(但是可以为 Empty)

@NotBlank

适用于 String 类型的数据上,加了@NotBlank 注解的参数不能为 Null 且 trim() 之后 size > 0;

不能用于Integer等数值类型上。

@NotEmpty

适用于 String、Collection集合、Map、数组等等,加了@NotEmpty 注解的参数不能为 Null 或者 长度为 0

注意事项和使用细节

  1. 如果校验注解不指定message属性,会返回默认消息,这些消息是配置在ValidationMessages_zh_CN.properties文件中的(使用Ctrl+N来进行查找)。这个文件是框架提供的。

  1. 在方法的参数前加@Validated,否则不进行验证。添加BindingResult result参数,用于显示错误信息。


@ControllerAdvise

引入背景:

可以发现,后端的验证,需要在每一个controller的path里面进行一次判断,重复量大,效率还低。

这个时候,我们可以使用@ControllerAdvice统一管理异常/错误。它是一个全局异常处理类/器。

关联回忆:

SpringMVC

使用方法:

  1. 新建一个类,并给它加上一个注解:@ControllerAdvice

  1. 在具体的方法上加注解:@ExceptionHandler({指定需要处理的异常类})

  1. 方法参数的类型跟,处理异常的类,是同一个

  1. 注意在controller层的注解@Validated依然需要,否则不起作用

/**
 * @ControllerAdvice 处理异常。该参数指向对哪个包生效
 * @Slf4j 输出日志
 * @ResponseBody 发送json格式的数据
 * @ExceptionHandler 参数说明对哪些异常生效
 */
@ControllerAdvice(basePackages = "com.stein.steinliving.commodity.controller")
@Slf4j
@ResponseBody
public class SteinLivingExceptionHandler {

    @ExceptionHandler({MethodArgumentNotValidException.class})
    public R exceptionHandler(MethodArgumentNotValidException e){
        HashMap map = new HashMap<>();
        e.getBindingResult().getFieldErrors().forEach(item->{
            map.put(item.getField(),item.getDefaultMessage());
        });
        return R.error(400,"验证异常,请按要求输入").put("data",map);
    }
}

验证异常也是异常的一种,所以引出异常的处理。

异常处理

Throwable.class是所有异常的父类,把它放在后面,这样前面没有处理的异常,都可以用它来接收。

通过Ctrl+H查看Throwable的继承关系,可以看到子类所有的异常类有哪些。这些类都是可以根据需要选填在@ExceptionHandler({MethodArgumentNotValidException.class})这个参数里面的

参数类型,这儿也是和注解的参数类型保持了一致。(同上)

    @ExceptionHandler({Throwable.class})
    public R errorHander(Throwable throwable){
        return R.error(40000,"系统未知错误");
    }

统一异常

统一规定错误/状态码异常

  1. 问题分析:公司写代码会统一规定错误/异常状态码和提示信息,我们可以将其抽取成枚举类,方便管理和扩展

  1. 错误/异常状态码如何规定,不同公司不太一样,遵守项目经理给的规则即可。比如模块编号+错误/异常编号

实现过程:

在common模组中创建Enum类,定义好要求的code和msg,再在原有代码的

R.error(枚举.xxx.getCode( ),枚举.xxx.getMsg( ) )即可

public enum EnumExceptionCode {
    UNKNOWN_EXCEPTION(40001,"系统未知异常"),
    INVALID_EXCEPTION(40000,"验证未通过");

    private int code;
    private String msg;

    EnumExceptionCode() {
    }

    EnumExceptionCode(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    getter and setter...

枚举的重点回忆:

a. 类的类型选择Enum枚举类型

b. 枚举的常量,写在类体的最开头。常量名建议大写。

c. 有多个常量,使用","逗号分隔

d. 常量的参数,跟构造器里面的一样。

分组校验

引入背景:

在不同的需要场景,校验规则不一样。比如在修改的时候,要校验id;而新增的时候,id不用校验。

使用方法及代码:

  1. 新建一个接口来定义一个分组,通过接口名来区分分组。

经测试,必须使用接口。如果是一个类,会报错。

public interface SaveGroup {
}
//这是两个接口的代码写在了一起
public interface UpdateGroup {
}
  1. 在调用的Controller的映射路径上,@Validated({添加分组的接口名.class}),来确定使用哪个分组

public R save(@Validated({SaveGroup.class}) @RequestBody BrandEntity brand)
public R update(@Validated({UpdateGroup.class})@RequestBody BrandEntity brand)
  1. 在Entity的校验规则注解上,添加group属性,确定加入哪些分组

    @NotBlank(message = "必须输入查询首字母",groups = {SaveGroup.class})
    @Pattern(regexp = "^[A-z]$",message = "必须是大小写的英文字母",groups={SaveGroup.class,UpdateGroup.class})
    private String firstLetter;
    /**
     * 排序
     */
    @NotNull(message = "必须输入能排序的正整数",groups = {SaveGroup.class})
//    @Pattern(regexp = "/^([1-9]\\d*|0)$/",message = "必须是正整数")这个写法会报错
    @Min(value = 0,message = "必须大于等于0",groups = {SaveGroup.class,UpdateGroup.class})
    private Integer sort;
  1. 注意:没有分组的Entity校验规则,在指定了@Validated({分组名})的映射路径下,不会生效!

    /**
     * logo
     * 这儿@NotBlank故意没有设置分组,用来说明此处注解不分组,而@Validated({设置了分组}),就不会生效
     */
    @NotBlank(message = "logo不能为空")
    @URL(message = "这不是一个url地址",groups={SaveGroup.class,UpdateGroup.class})
    private String logo;

自定义校验器

引入背景:

@Pattern 正则表达式的校验不能用在数值类型上面,比如Integer。原有注解又不能满足定制需要,比如isshow是否显示,要求验证只能输入0-1两个值。

代码+配置实现

  1. 说明:自定义校验器/注解,全程可以参考@NotNull源码来编写

  1. 因为这个校验在各个模块都可能使用,因此在steinliving-common模块开发

  1. 修改steinliving\steinliving-common\pom.xml,引入自定义校验器相关jar

        
        
            javax.validation
            validation-api
            2.0.1.Final
        
  1. 新建一个自定义的注解,比如@EnumValidated。内部结构可以参考@NotNull

  1. 添加values属性,注意类型

  1. 添加message的默认值。文件名src/main/resources/ValidationMessages.properties

该资源的名字必须以此命名,校验器才能找到它,配置才能生效。

赋值必须使用unicode

com.stein.common.validate.EnumValidated.message=\u7ed3\u679c\u53ea\u80fd\u662f0\u6216\u80051

  1. 新建自定义验证类实现接口

偶然学到的一个小技巧:

在IDEA中,使用Alt+左键拖选,可以选择局部多行,简直不要太便捷。

你可能感兴趣的:(项目,java,spring,cloud)