前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >帮你少写一大半参数校验代码的小技巧

帮你少写一大半参数校验代码的小技巧

作者头像
Java识堂
发布2019-10-24 19:53:27
7180
发布2019-10-24 19:53:27
举报
文章被收录于专栏:Java识堂

介绍

几乎每个web网站都会对用户提交的参数进行校验,前端要做,后端也要做。防止用户直接通过接口调用的方式来请求或保存数据,从而导致产生脏数据等其他严重的后果。

因为有些校验的逻辑也很繁琐,为了减轻开发者的负担,Java发布了 JSR303/JSR-349数据校验规范

JSR303 是一项标准,JSR-349 是其的升级版本,添加了一些新特性,他们规定一些校验规范即校验注解,如 @Null,@NotNull,@Pattern,他们位于 javax.validation.constraints 包下,只提供规范不提供实现。

而 hibernate validation 是对这个规范的实践(不要将 hibernate 和数据库 orm 框架联系在一起),他提供了相应的实现,并增加了一些其他校验注解,如 @Length,@Range 等等,他们位于 org.hibernate.validator.constraints 包下。

而万能的 spring 为了给开发者提供便捷,对 hibernate validation 进行了二次封装,显示校验 validated bean 时,你可以使用 spring validation 或者 hibernate validation,而 spring validation 另一个特性,便是其在 springmvc 模块中添加了自动校验,并将校验信息封装进了特定的类中。这无疑便捷了我们的 web 开发

在spring boot项目中只要加入如下依赖即可使用校验注解

代码语言:javascript
复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

查看子依赖会发现有如下依赖

代码语言:javascript
复制
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>

JSR提供的部分校验注解如下

注解

解释

@Null

被注释的元素必须为 null

@NotNull

被注释的元素必须不为 null

@AssertTrue

被注释的元素必须为 true

@AssertFalse

被注释的元素必须为 false

@Min

被注释的元素必须是一个数字,其值必须大于等于指定的最小值

@Max

被注释的元素必须是一个数字,其值必须小于等于指定的最大值

@Pattern

被注释的元素必须符合指定的正则表达式

@Email

被注释的元素必须是电子邮箱地址

经常被搞混的3个注解

注解

解释

@NotNull

不能为null,但可以为empty

@NotEmpty

不能为null,而且长度必须大于0

@NotBlank

只能作用在String上,不能为null,而且调用trim()后,长度必须大于0

我来举一个org.apache.commons.lang3.StringUtils中的例子,你就能理解NotBlank的意思了,如下断言都能测试通过

代码语言:javascript
复制
assertEquals(true, StringUtils.isBlank(null));
assertEquals(true, StringUtils.isBlank(""));
// 空格
assertEquals(true, StringUtils.isBlank(" "));
// 回车
assertEquals(true, StringUtils.isBlank("    "));

改造一个注册的接口

先定义一下状态枚举类

代码语言:javascript
复制
public enum ResponseCode {

    SUCCESS(0, "成功"),
    ERROR(1, "失败"),
    ILLEGAL_ARGUMENT(2, "参数错误"),
    EMPTY_RESULT(3, "结果为空"),
    NEED_LOGIN(10, "需要登录");

    private final int code;
    private final String desc;

    ResponseCode(int code, String desc) {
        this.code = code;
        this.desc = desc;
    }

    public int getCode() {
        return code;
    }

    public String getDesc() {
        return desc;
    }
}

定义项目的返回对象

代码语言:javascript
复制
@Data
/** 注解的作用是序列化json时,如果是null对象,key也会消失 */
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ServerResponse implements Serializable {

    /** 状态值 **/
    private int status;
    /** 描述 **/
    private String msg;
    /** 数据 **/
    private Object data;

    public ServerResponse(int status, String msg) {
        this.status = status;
        this.msg = msg;
    }

    public static ServerResponse success() {
        return new ServerResponse(ResponseCode.SUCCESS.getCode(), ResponseCode.SUCCESS.getDesc());
    }

    public static ServerResponse illegalArgument(String msg) {
        return new ServerResponse(ResponseCode.ILLEGAL_ARGUMENT.getCode(), msg);
    }
}

注册接口,这里省略了一部分校验

代码语言:javascript
复制
@RequestMapping("regist")
public ServerResponse regist(@RequestParam("name") String name,
                             @RequestParam("phone") String phone,
                             @RequestParam("phone") String email) {
    if (StringUtils.isBlank(name)) {
        return ServerResponse.illegalArgument("用户名不能为空");
    }
    // 其他一堆校验过程,调用service
    return ServerResponse.success();
}

当不满足条件时返回如下

代码语言:javascript
复制
{
    "status": 2,
    "msg": "用户名不能为空"
}

当参数较多,校验的逻辑也越来越多,这时可以直接将前端传过来参数直接转为对象

代码语言:javascript
复制
@Data
public class Student {

    @NotBlank(message = "用户名不能为空")
    private String name;
    @NotBlank(message = "手机号不能为空")
    @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$", message = "手机号码格式错误")
    private String phone;
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式错误")
    private String email;
}
代码语言:javascript
复制
@RequestMapping("regist")
public ServerResponse regist(@Valid Student student, BindingResult bindingResult) {
    if (bindingResult.hasErrors()) {
        List<FieldError> errorList = bindingResult.getFieldErrors();
        // list不为空
        if (CollectionUtils.isNotEmpty(errorList)) {
            return ServerResponse.illegalArgument(errorList.get(0).getDefaultMessage());
        }
    }
    // 调用service
    return ServerResponse.success();
}

代码将不满足条件的字段的描述取一个出来返回,类似如下。当都满足时才会返回成功

代码语言:javascript
复制
{
    "status": 2,
    "msg": "用户名不能为空"
}

需要注意的地方如下

参数 Student 前需要加上@Valid或@Validated 注解(下文说这2个注解的区别),表明需要 spring 对其进行校验,而校验的信息会存放到其后的 BindingResult 中。注意,必须相邻,如果有多个参数需要校验,形式可以如下。foo(@Validated Student student, BindingResult studentBindingResult ,@Validated Bar bar, BindingResult barBindingResult); 即一个校验类对应一个校验结果。

@Validated比@Valid的功能更强大

@Validated比@Valid的功能更强大,主要体现在@Validated可以进行分组校验和嵌套校验。 如果同一个类,在不同的使用场景下有不同的校验规则,那么可以使用分组校验,用处不多,不再介绍。介绍一下嵌套校验

假如我们还要求学生填写父亲和母亲的名字(并且不能为空),而后端的设计中父亲和母亲的名字被放在另一个类Relation中,这就要求我们能进行嵌套校验。

我们要做如下2个事情 1.将方法中的@Valid注解改为@Validated 2.在relation属性上加@Valid注解

代码语言:javascript
复制
@Data
public class Student {

    @NotBlank(message = "用户名不能为空")
    private String name;
    @NotBlank(message = "手机号不能为空")
    @Pattern(regexp = "^1(3|4|5|7|8)\\d{9}$", message = "手机号码格式错误")
    private String phone;
    @NotBlank(message = "邮箱不能为空")
    @Email(message = "邮箱格式错误")
    private String email;
    @NotNull(message = "父母名字不能为空")
    @Valid
    private Relation relation;
}
代码语言:javascript
复制
@Data
public class Relation {

    @NotBlank(message = "父亲的姓名不能为空")
    private String fatherName;
    @NotBlank(message = "父亲的姓名不能为空")
    private String motherName;
}

那Relation类的fatherName属性如何被赋值呢? 很简单,前端传入的参数名是如下即可

代码语言:javascript
复制
xxxx?relation.fatherName=li&relation.motherName=liu
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-10-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Java识堂 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 改造一个注册的接口
  • @Validated比@Valid的功能更强大
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档