系统设计-参数还能这么优雅校验?

点击上方名片关注我,为你带来更多踩坑案例

d1dcdea785b84fe1df5e0f962b154030.png

- 引言 -

    如果你是一个摸爬滚打几年的开发者,那么这个阶段,对系统设计的合理性绝对是衡量一个人水平的重要标准。

    一个好的设计不光能让你工作中避免很多麻烦,还能为你面试的时候增加很多谈资

    而且,不同设计之间理念都是有借鉴性参考性的,你见过的设计多了,思考的多了,再次面临一个问题的时候,就会有很多点子不由自主的冒出来。

    希望这个系列的文章,能够和大家互相借鉴参考,共同进步。

- 初级参数校验 -

    很简单的场景

    我要写一个接口,需要校验接口参数,比如编辑一条数据的时候id不能为空

    很直白的一个做法就是写一个参数校验方法

if(id == null){
  // 抛出异常
}


    不用怀疑,这是可行的,也没什么性能问题,就是不够优雅而已,所以我们为了更优雅一些,少些点业务代码,可以进行一定程度的优化

- 中级参数校验 -

    类上加@Validated注解

@Validated
@RestController
public class PeopleListController {
}

    接口参数中加@Valid注解

@ApiOperation("编辑人物")
@PutMapping
public ApiResponse<JSONArray> edit(@RequestBody @Valid @ApiParam("人物") PeopleListEditParam peopleListEditParam) {
    PeopleListEntity edit = peopleListService.edit(peopleListEditParam);
    return ApiResponse.success(edit);
}

    可以支持单个对象也可以支持数组

    入参对象中写上校验注解

@NotNull(message = "id不能为空!")
private Long id;

    类似的还有@NotBlank等等,这是spring自带的一套校验注解,使用起来比较简单。

    校验结果如下:

1285a1e96d5a702d0f14cf72fd4b10fb.png

什么都不管的话会把这一坨直接抛出去,不太友好,虽然硬看也能看懂。

有一个方法就是,在对应api中,有个BindResult对象可以接收到这一坨,然后把我们需要的信息抛出即可。

但是我觉得这样太冗余太麻烦了。

可以加一个全局异常处理类,这个类很有用,不光是体现在参数校验的时候

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
    /**
     * 参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @SuppressWarnings("unchecked")
    public ApiResponse<Object> handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) {
        BindingResult bindingResult = e.getBindingResult();
        List<ObjectError> allErrors = bindingResult.getAllErrors();
        List<String> lists = new ArrayList<>();
        for (ObjectError objectError : allErrors) {
            lists.add(objectError.getDefaultMessage());
        }
        return ApiResponse.fail(Constants.CODE_ERROR_PARAM, lists.toString());
    }
}

    增加如上代码后,校验结果就变成了

4f52dc759d4f1758aa93b250728f4fb6.png

    也可以多个参数同时校验

d72789761bddc52112442f5eef343585.png

- 进阶参数校验 -

    id不能为空,姓名不能为空之类的判断很好做,也可以用spring自带的@NotNull、@NotEmpty之类的来做。

    但是如上,性别不合法这种比较自定义的校验怎么做呢?

    使用注解+枚举来实现

    首先定义一个注解

/**
 * description 验证字段的枚举值
 * @author LuHui
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD})
@Constraint(validatedBy = EnumValidatorClass.class)
public @interface EnumValidator {


    Class<?> value();


    String message() default "当前枚举值不合法";


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


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

    然后给我们的注解增加对应的校验逻辑处理类

public class EnumValidatorClass implements ConstraintValidator<EnumValidator, Object>, Annotation {


    private final List<Object> values = new ArrayList<>();


    @Override
    public Class<? extends Annotation> annotationType() {
        return null;
    }


    /**
     * description: 初始化枚举值
     *
     * @param enumValidator 注解对象
     * @author luhui
     * @date 2022/10/26 20:53
     */
    @Override
    public void initialize(EnumValidator enumValidator) {
        Class<?> clazz = enumValidator.value();
        Object[] objects = clazz.getEnumConstants();
        values.addAll(Arrays.asList(objects));
    }


    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if (value == null) {
            return true;
        }
        // 校验值
        for (Object o : values) {
            boolean equals = o.toString().equals(value.toString());
            if (equals) {
                return true;
            }
        }
        return false;
    }
}

    然后我们以性别为例,增加一个性别枚举类

/**
 * description 性别枚举类
 *
 * @author LuHui
 */
public enum SexEnum {
    // 男性
    MAN(1, "男"),
    // 女性
    WOMAN(2, "女"),
    // 未知(比如一些不具备性别的人物)
    UN_KNOWN(3, "无");


    private static final HashMap<Integer, SexEnum> SEX_MAP = new HashMap<>();


    static {
        for (SexEnum e : SexEnum.values()) {
            SEX_MAP.put(e.getValue(), e);
        }
    }


    @Getter
    private final Integer value;
    @Getter
    private final String desc;


    SexEnum(Integer value, String desc) {
        this.value = value;
        this.desc = desc;
    }


    @Override
    public String toString() {
        // 为校验,枚举类需重写toString
        if (value == null) {
            return "";
        }
        return this.getValue().toString();
    }


    public static SexEnum find(Integer value) {
        return SEX_MAP.get(value);
    }
}

    最后一步,在接收参数的实体中,增加校验注解,也就是我们自定义的注解

@EnumValidator(value = SexEnum.class, message = "性别不合法")
@ApiModelProperty("性别")
private Integer sex;

    然后就大功告成了,快去看看你的接口用不用得到吧。

    如果觉得我写的东西对您有一点帮助,可以关注下,会持续给大家更新一些小知识!感谢!

- 结束语 -

    优雅的接口设计虽然不一定能带来多少立竿见影的性能上的提升,但是对我们的系统的影响是长远的,对开发者来说也是很有成就感的事情,毕竟大家都不想写一坨屎给别人,虽然这是所有系统的归宿。

猜你喜欢

转载自blog.csdn.net/qq_31363843/article/details/128030898