【业务功能篇78】微服务-前端后端校验- 统一异常处理-JSR-303-validation注解

49 篇文章 2 订阅
46 篇文章 1 订阅

5. 前端校验

我们在前端提交的表单数据,我们也是需要对提交的数据做相关的校验的

Form 组件提供了表单验证的功能,只需要通过 rules 属性传入约定的验证规则,并将 Form-Item 的 prop 属性设置为需校验的字段名即可

image.png

校验的页面效果

image.png

前端数据校验就搞定了。后端校验也是不可避免的

6. 后端服务校验

6.1 JSR-303介绍

  JSR是Java Specification Requests的缩写,意思是Java 规范提案。是指向JCP(Java Community Process)提出新增一个标准化技术规范的正式请求。任何人都可以提交JSR,以向Java平台增添新的API和服务。JSR已成为Java界的一个重要标准。

  JSR-303 是JAVA EE 6 中的一项子规范,叫做Bean Validation,Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。
  Hibernate Validator 是 Bean Validation 的参考实现 . Hibernate Validator 提供了 JSR 303 规范中所有内置 constraint 的实现,除此之外还有一些附加的 constraint。

在这里插入图片描述

Hibernate 中填充一部分

在这里插入图片描述

6.2 后端校验实现

1.需要在commons服务中添加对应的依赖

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
            <version>2.4.12</version>
        </dependency>

2.在需要校验的Bean的字段头部添加对应的注解

image.png

3.通过@Valid注解来开启JSR303的校验

image.png

4.测试,通过postman提交空的数据

image.png

当我们提交一个非空的数据是可以通过的

image.png

5.校验不合法的提示信息,在Controller中通过 BindingResult对象来获取校验的结果信息,然后解析出来后封装为R对象响应

    @RequestMapping("/save")
    //@RequiresPermissions("product:brand: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();
    }

image.png

然后完善其他字段你的校验规则

@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
	private static final long serialVersionUID = 1L;

	/**
	 * 品牌id
	 */
	@TableId
	private Long brandId;
	/**
	 * 品牌名
	 */
	//@NotEmpty
	//@NotNull
	@NotBlank(message = "品牌的名称不能为空")
	private String name;
	/**
	 * 品牌logo地址
	 */
	@NotBlank(message = "logo不能为空")
	@URL(message = "logo必须是一个合法URL地址")
	private String logo;
	/**
	 * 介绍
	 */
	private String descript;
	/**
	 * 显示状态[0-不显示;1-显示]
	 */
	private Integer showStatus;
	/**
	 * 检索首字母
	 */
	@NotBlank(message = "检索首字母不能为空")
	@Pattern(regexp = "/^[a-zA-Z]$/",message = "检索首字母必须是单个的字母")
	private String firstLetter;
	/**
	 * 排序
	 */
	@NotNull(message = "排序不能为null")
	@Min(value = 0,message = "排序不能小于0")
	private Integer sort;

}

image.png

6.3 统一的异常处理

  在SpringMVC中的统一异常处理我们通过ControllerAdvice来处理

/**
 * 统一的异常处理类
 */
/*@ResponseBody
@ControllerAdvice*/
@Slf4j
@RestControllerAdvice(basePackages = "com.msb.mall.product.controller")
public class ExceptionControllerAdvice {
    /**
     * 处理验证异常的方法
     * @param e
     */

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handlerValidExecption(MethodArgumentNotValidException e){
        Map<String,String> map = new HashMap<>();
        e.getFieldErrors().forEach((fieldError)->{
            map.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        return R.error(400,"提交的数据不合法").put("data",map);
    }

    /**
     * 系统其他的异常处理
     * @param throwable
     * @return
     */
    @ExceptionHandler(Throwable.class)
    public R handlerExecption(Throwable throwable){
        log.error("错误信息:",throwable);
        return R.error(400,"未知异常信息").put("data",throwable.getMessage());
    }
}

响应编码的规制制订,因为随着后面的业务越来越复杂,我们在响应异常信息的时候尽量准确的给客户端有用的提示信息。

通用的错误列表,响应的编码统一为5位数字,前面两位约定为业务场景,最后三位约定为错误码

10:表示通用

/001:参数格式错误 10001

/002:未知异常 10002

11:商品

12:订单

13:物流

14:会员

定义对应的枚举类

package com.msb.common.exception;

/**
 * 错误编码和错误信息的枚举类
 */
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;
    }
}

在统一异常处理中我们就可以使用通用的编码

package com.msb.mall.product.exception;

import com.msb.common.exception.BizCodeEnume;
import com.msb.common.utils.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * 统一的异常处理类
 */
/*@ResponseBody
@ControllerAdvice*/
@Slf4j
@RestControllerAdvice(basePackages = "com.msb.mall.product.controller")
public class ExceptionControllerAdvice {
    /**
     * 处理验证异常的方法
     * @param e
     */

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public R handlerValidExecption(MethodArgumentNotValidException e){
        Map<String,String> map = new HashMap<>();
        e.getFieldErrors().forEach((fieldError)->{
            map.put(fieldError.getField(),fieldError.getDefaultMessage());
        });
        //return R.error(400,"提交的数据不合法").put("data",map);
        return R.error(BizCodeEnume.VALID_EXCEPTION.getCode(), BizCodeEnume.VALID_EXCEPTION.getMsg())
                .put("data",map);
    }

    /**
     * 系统其他的异常处理
     * @param throwable
     * @return
     */
    @ExceptionHandler(Throwable.class)
    public R handlerExecption(Throwable throwable){
        log.error("错误信息:",throwable);
        //return R.error(400,"未知异常信息").put("data",throwable.getMessage());
        return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg())
                .put("data",throwable.getMessage());
    }
}

image.png

6.4 分组校验

  在实际的业务场景中同一个Entity的校验可能会有不同的规则,比如添加数据品牌id必须为空,而更新数据品牌Id必须不为空,针对这种情况我们需要使用分组校验来实现

1>定义标志类

image.png

2>在Entity中指定分组规则

image.png

3>通过@Validated注解来实现分组校验

image.png

6.5 自定义校验注解

  面临特殊的校验需要我们可以通过正则表达式来处理,当然我们也可以通过自定义校验注解的方式来实现。

1> 创建自定义的校验注解

/**
 * 自定义的校验注解
 */
@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {

    String message() default "{com.msb.common.valid.ListValue.message}";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };

    int[] val() default {};
}

对应需要创建提示信息的属性文件

image.png

2>创建一个自定义的校验器

package com.msb.common.valid;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.annotation.Annotation;
import java.sql.ClientInfoStatus;
import java.util.HashSet;

/**
 * 对应的校验注解的校验器
 */
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>关联自定义的校验注解和校验器

image.png

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值