0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

SpringBoot Web应用如何进行参数校验?(下)

jf_78858299 来源:JAVA旭阳 作者:JAVA旭阳 2023-05-11 10:37 次阅读

3. 分组校验

一个VO对象在新增的时候某些字段为必填,在更新的时候又非必填。如上面的ValidVO中 id`` 和 appId 属性在新增操作时都是 非必填 ,而在编辑操作时都为 必填 ,name在新增操作时为 必填 ,面对这种场景你会怎么处理呢? 在实际开发中我见到很多同学都是建立两个VO对象,ValidCreateVOValidEditVO来处理这种场景,这样确实也能实现效果,但是会造成类膨胀。

其实Validator校验框架已经考虑到了这种场景并且提供了解决方案,就是 分组校验 ,只不过很多同学不知道而已。

要使用分组校验,只需要三个步骤。

3.1. 第一步,定义分组接口

public interface ValidGroup extends Default {

    interface Crud extends ValidGroup{
        interface Create extends Crud{

        }

        interface Update extends Crud{

        }

        interface Query extends Crud{

        }

        interface Delete extends Crud{

        }
    }
}

这里我们定义一个分组接口ValidGroup让其继承javax.validation.groups.Default,再在分组接口中定义出多个不同的操作类型,CreateUpdateQueryDelete

3.2. 第二步,在模型中给参数分配分组

@Data
public class ValidVO {
    @Null(groups = ValidGroup.Crud.Create.class)
    @NotNull(groups = ValidGroup.Crud.Update.class, message = "应用ID不能为空")
    private String id;

    @Length(min = 6,max = 12,message = "appId长度必须位于6到12之间")
    @Null(groups = ValidGroup.Crud.Create.class)
    @NotNull(groups = ValidGroup.Crud.Update.class, message = "应用ID不能为空")
    private String appId;

    @NotBlank(message = "名字为必填项")
    @NotBlank(groups = ValidGroup.Crud.Create.class,message = "名字为必填项")
    private String name;

    @Email(message = "请填写正确的邮箱地址")
    private String email;

    @EnumString(value = {"F","M"}, message="性别只允许为F或M")
    private String sex;

    @NotEmpty(message = "级别不能为空")
    private String level;
}

给参数指定分组,对于未指定分组的则使用的是默认分组。

3.3. 第三步,给需要参数校验的方法指定分组

@PostMapping(value = "/valid/add")
public String add(@Validated(value = ValidGroup.Crud.Create.class) ValidVO validVO){
 log.info("validEntity is {}", validVO);
 return "test3 valid success";
}

@PostMapping(value = "/valid/update")
public String update(@Validated(value = ValidGroup.Crud.Update.class) ValidVO validVO){
 log.info("validEntity is {}", validVO);
 return "test4 valid success";
}

这里我们通过value属性给add()update()方法分别指定CreateUpdate分组

3.4. 测试

POST http://localhost:8080/valid/add
Content-Type: application/x-www-form-urlencoded

name=javadaily&level=12&email=476938977@qq.com&sex=F
  • Create操作

在Create时我们 没有传递id和appId参数校验通过。

{
  "status": 100,
  "message": "操作成功",
  "data": "test3 valid success",
  "timestamp": 1652186105359
}
  • update操作

使用同样的参数调用update方法时则提示参数校验错误

{
  "status": 400,
  "message": "ID不能为空; 应用ID不能为空",
  "data": null,
  "timestamp": 1652186962377
}
复制代码
  • 默认校验生效操作

由于email属于默认分组,而我们的分组接口ValidGroup已经继承了Default分组,所以也是可以对email字段作参数校验的。故意写错email格式

POST http://localhost:8080/valid/add
Content-Type: application/x-www-form-urlencoded

/valid/update?name=javadaily&level=12&email=476938977&sex=F
{
  "status": 400,
  "message": "请填写正确的邮箱地址; ID不能为空; 应用ID不能为空",
  "data": null,
  "timestamp": 1652187273865
}

4. 业务规则校验

业务规则校验指接口需要满足某些特定的业务规则,举个例子:业务系统的用户需要保证其唯一性,用户属性不能与其他用户产生冲突,不允许与数据库中任何已有用户的用户名称、手机号码、邮箱产生重复。 这就要求在 创建用户时需要校验用户名称、手机号码、邮箱是否被注册编辑用户时不能将信息修改成已有用户的属性最优雅的实现方法应该是参考 **Bean Validation** 的标准方式,借助自定义校验注解完成业务规则校验。

4.1. 自定义注解

首先我们需要创建两个自定义注解,用于业务规则校验:

  • UniqueUser:表示一个用户是唯一的,唯一性包含:用户名,手机号码、邮箱
@Documented
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
@Constraint(validatedBy = UserValidation.UniqueUserValidator.class)
public @interface UniqueUser {

    String message() default "用户名、手机号码、邮箱不允许与现存用户重复";

    Class?[] groups() default {};

    Class? extends Payload[] payload() default {};
}
  • NotConflictUser:表示一个用户的信息是无冲突的,无冲突是指该用户的敏感信息与其他用户不重合
@Documented
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
@Constraint(validatedBy = UserValidation.NotConflictUserValidator.class)
public @interface NotConflictUser {
    String message() default "用户名称、邮箱、手机号码与现存用户产生重复";

    Class?[] groups() default {};

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

4.2. 实现业务校验规则

想让自定义验证注解生效,需要实现 ConstraintValidator 接口。接口的第一个参数是 自定义注解类型 ,第二个参数是 被注解字段的类 ,因为需要校验多个参数,我们直接传入用户对象。 需要提到的一点是 ConstraintValidator 接口的实现类无需添加 @Component 它在启动的时候就已经被加载到容器中了。

@Slf4j
public class UserValidation<T extends Annotation> implements ConstraintValidator<T, User> {

    protected Predicate

这里使用Predicate函数式接口对业务规则进行判断。

4.3. 测试代码

@RestController
@RequestMapping("/senior/user")
@Slf4j
@Validated
public class UserController {
    @Autowired
    private UserRepository userRepository;


    @PostMapping
    public User createUser(@UniqueUser @Valid User user){
        User savedUser = userRepository.save(user);
        log.info("save user id is {}",savedUser.getId());
        return savedUser;
    }

    @SneakyThrows
    @PutMapping
    public User updateUser(@NotConflictUser @Valid @RequestBody User user){
        User editUser = userRepository.save(user);
        log.info("update user is {}",editUser);
        return editUser;
    }
}

使用很简单,只需要在方法上加入自定义注解即可,业务逻辑中不需要添加任何业务规则的代码。

POST http://localhost:8080/valid/add
Content-Type: application/json

    /senior/user

{
    "userName" : "100001"
}
{
	"status": 400,
	"message": "用户名、手机号码、邮箱不允许与现存用户重复",
	"data": null,
	"timestamp": 1652196524725
}

5. 总结

通过上面几步操作,业务校验便和业务逻辑就完全分离开来,在需要校验时用@Validated注解自动触发,或者通过代码手动触发执行,可根据你们项目的要求,将这些注解应用于控制器、服务层、持久层等任何层次的代码之中。 这种方式比任何业务规则校验的方法都优雅,推荐大家在项目中使用。在开发时可以将不带业务含义的格式校验注解放到 Bean 的类定义之上,将带业务逻辑的校验放到 Bean 的类定义的外面。这两者的区别是放在类定义中的注解能够自动运行,而放到类外面则需要像前面代码那样,明确标出注解时才会运行。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 接口
    +关注

    关注

    33

    文章

    7648

    浏览量

    148533
  • 代码
    +关注

    关注

    30

    文章

    4556

    浏览量

    66820
  • Validator验
    +关注

    关注

    0

    文章

    3

    浏览量

    5761
  • SpringBoot
    +关注

    关注

    0

    文章

    172

    浏览量

    106
收藏 人收藏

    评论

    相关推荐

    SpringBoot知识总结

    SpringBoot干货学习总结
    发表于 08-01 10:40

    请问windowshost文件如何进行修改与刷新?

    windowshost文件如何进行修改与刷新
    发表于 11-10 07:00

    请教一何进行毫米波测量?

    请教一何进行毫米波测量?
    发表于 05-12 06:21

    变频器的参数何进行设置呢

    变频器的参数有数百甚至上千个,对这些参数进行合理正确的设置是使变频器高效运行并且满足用户要求的前提,那么,如何进行设置呢?本文以西门子MicroMaster440变频器为例
    发表于 09-03 07:43

    请问一何进行面向对象编程?

    请问一何进行面向对象编程?
    发表于 09-18 06:16

    Ubuntu耳机电流声如何进行消除

    Ubuntu耳机电流声如何进行消除
    发表于 10-13 07:56

    怎样去使用springboot

    怎样去使用springboot呢?学习springboot需要懂得哪些?
    发表于 10-25 07:13

    SpringBoot应用启动运行run方法

    )、refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;如果是web应用创建**AnnotationConfigEmbeddedWebApplicationContext**,否则
    发表于 12-20 06:16

    Springboot是如何获取自定义异常并进行返回的

    这里看到新服务是封装的自定义异常,准备入手剖析一,自定义的异常是如何进行抓住我们请求的方法的异常,并进行封装返回到。废话不多说,先看看如何才能实现封装异常,先来一个示例:在这里,您会看到新服务是一
    发表于 03-22 14:15

    何进行OPCDCOM配置

    何进行OPCDCOM配置(四会理士电源技术有限公司招聘)-如何进行OPCDCOM配置                      
    发表于 09-18 14:23 11次下载
    如<b class='flag-5'>何进行</b>OPCDCOM配置

    何进行电源设计 - 第1部分

    何进行电源设计 - 第1部分
    发表于 11-02 08:16 1次下载
    如<b class='flag-5'>何进行</b>电源设计 - 第1部分

    如何用责任链默认优雅地进行参数校验

    那么有什么更好的参数校验的方式呢?本文就推荐一种通过责任链设计模式来优雅地实现参数校验功能,我们通过一个用户注册的例子来讲明白如何实现。
    的头像 发表于 04-06 15:00 321次阅读

    什么是 SpringBoot

    本文从为什么要有 `SpringBoot`,以及 `SpringBoot` 到底方便在哪里开始入手,逐步分析了 `SpringBoot` 自动装配的原理,最后手写了一个简单的 `start` 组件,通过实战来体会了 `
    的头像 发表于 04-07 11:28 1037次阅读
    什么是 <b class='flag-5'>SpringBoot</b>?

    SpringBoot Web应用如何进行参数校验?(上)

    的话就太繁琐了,代码可读性极差。**Validator框架**就是为了解决开发人员在开发的时候少写代码,提升开发效率;Validator专门用来进行接口参数校验,例如常见的必填校验,e
    的头像 发表于 05-11 10:31 449次阅读

    javaweb和springboot能一起用吗

    框架来开发 Web 应用程序。 首先,让我们了解一下 JavaWeb 和 SpringBoot 的基本概念。 JavaWeb 是一种用于开发基于 Java 技术的 Web 应用程序的技术框架。它提供
    的头像 发表于 11-16 10:54 923次阅读