首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

【超实用攻略】SpringBoot + validator 轻松实现全注解式的参数校验

编程知识
2024年07月24日 09:38

一、故事背景

关于参数合法性验证的重要性就不多说了,即使前端对参数做了基本验证,后端依然也需要进行验证,以防不合规的数据直接进入服务器,如果不对其进行拦截,严重的甚至会造成系统直接崩溃

本文结合自己在项目中的实际使用经验,主要以实用为主,对数据合法性验证做一次总结,不了解的朋友可以学习一下,同时可以立马实践到项目上去。

下面我们通过几个示例来演示如何判断参数是否合法,废话不多说,直接撸起来!

二、断言验证

对于参数的合法性验证,最初的做法比较简单,自定义一个异常类。

public class CommonException extends RuntimeException {

    private Integer code;

    public Integer getCode() {
        return code;
    }

    public CommonException(String message) {
        super(message);
        this.code = 500;
    }

    public CommonException(Integer code, String message) {
        super(message);
        this.code = code;
    }
}

当检查到某个参数不合法的时候,直接抛异常!

@RestController
public class HelloController {

    @RequestMapping("/upload")
    public void upload(MultipartFile file) {
        if (file == null) {
            throw new CommonException("请选择上传文件!");
        }
        //.....
    }
}

最后写一个统一异常拦截器,对抛异常的逻辑进行兜底处理。

这种做法比较简单直观,如果当前参数既要判断是否为空,又要判断长度是否超过最大限制的时候,代码就会显得很臃肿,而且复用性很差

于是,程序界的大佬想到了一个更加优雅又能节省代码的方式,创建一个断言类工具类,专门用来判断参数的是否合法,如果不合法就抛异常,示例如下:

/**
 * 断言工具类
 */
public abstract class LocalAssert {
    
    public static void isTrue(boolean expression, String message) throws CommonException {
        if (!expression) {
            throw new CommonException(message);
        }
    }
    public static void isStringEmpty(String param, String message) throws CommonException{
        if(StringUtils.isEmpty(param)) {
            throw new CommonException(message);
        }
    }

    public static void isObjectEmpty(Object object, String message) throws CommonException {
        if (object == null) {
            throw new CommonException(message);
        }
    }

    public static void isCollectionEmpty(Collection coll, String message) throws CommonException {
        if (coll == null || (coll.size() == 0)) {
            throw new CommonException(message);
        }
    }
}

当我们需要对参数进行验证的时候,直接通过这个类就可以完成,示例如下:

@RestController
public class HelloController {

    @RequestMapping("/save")
    public void save(String name, String email) {
        LocalAssert.isStringEmpty(name, "用户名不能为空!");
        LocalAssert.isStringEmpty(email, "邮箱不能为空!");
        
        //.....
    }
}

相比上面的实现方式,这种处理逻辑,代码明显要简洁的多!

类似这样的工具类还很多,比如spring也提供了一个名为Assert的断言工具类,在开发的时候,可以直接使用!

三、注解验证

下面我们要介绍的是另一种更简洁的参数验证逻辑,使用注解来对数据进行合法性验证,不仅代码会变得很简洁,阅读起来也十分令人赏心悦目!

以 Spring Boot 工程为例,下面我们一起来看看具体的实践方式。

3.1、添加依赖包

首先在pom.xml中引入spring-boot-starter-web依赖包即可,它会自动将注解验证相关的依赖包打入工程!

<!-- spring boot web -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

3.2、编写注解校验请求对象

接着创建一个实体User,用于封装用户注册时的请求参数,并在参数属性上添加对应的注解验证规则

import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Size;

public class User {

    @NotBlank(message = "用户名不能为空!")
    private String userName;

    @Email(message = "邮箱格式不正确")
    @NotBlank(message = "邮箱不能为空!")
    private String email;

    @NotBlank(message = "密码不能为空!")
    @Size(min = 8, max = 16,message = "请输入长度在8~16位的密码")
    private String userPwd;

    @NotBlank(message = "确认密码不能为空!")
    private String confirmPwd;

    // set、get方法等...
}

3.3、编写请求接口

web层创建一个register()注册接口方法,同时在请求参数上添加@Valid注解,示例如下:

import javax.validation.Valid;

@RestController
public class UserController {

    @RequestMapping("/register")
    public ResultMsg register(@RequestBody @Valid User user){
        if(!user.getUserPwd().equals(user.getConfirmPwd())){
            throw new CommonException(4001, "确认密码与密码不相同,请确认!");
        }
        //业务处理...
        return ResultMsg.success();
    }
}

3.4、编写全局异常处理器

最后自定义一个异常全局处理器,用于处理异常逻辑,如下:

@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 拦截Controller层的异常
     * @param e
     * @return
     */
    @ExceptionHandler(value = {Exception.class})
    @ResponseBody
    public Object exceptionHandler(HttpServletRequest request, Exception e){
        LOGGER.error("【统一异常拦截】请求地址:{}, 错误信息:{}", request.getRequestURI(), e.getMessage());
        // 注解验证抛出的异常
        if(e instanceof MethodArgumentNotValidException){
            // 获取错误信息
            String error = ((MethodArgumentNotValidException) e).getBindingResult().getFieldError().getDefaultMessage();
            return ResultMsg.fail(500, error);
        }
        // 自定义抛出的异常
        if(e instanceof CommonException){
            return ResultMsg.fail(((CommonException) e).getCode(), e.getMessage());
        }
        return ResultMsg.fail(999, e.getMessage());
    }
}

统一响应对象ResultMsg,如下:

public class ResultMsg<T> {

    /**状态码**/
    private int code;

    /**结果描述**/
    private String message;

    /**结果集**/
    private T data;

    /**时间戳**/
    private long timestamp;

    // set、get方法等...
}

3.5、服务测试

启动项目,使用postman来验证一下代码的正确性,看看效果如何?

  • 测试字段是否为空

  • 测试邮箱是否合法

  • 测试密码长度是否符合要求

  • 测试密码与确认密码是否相同

可以看到,验证结果与预期一致!

四、自定义注解验证

事实上,熟悉 SpringMVC 源码的同学可能知道,Spring Boot 内置了一个hibernate-validator校验组件,上文就是利用它来完成对请求时入参上的注解验证。

默认的情况下,依赖包已经给我们提供了非常多的校验注解,如下!

  • JSR 提供的校验注解!

  • Hibernate Validator 提供的校验注解

但是某些情况,例如性别这个参数,可能需要我们自己去手动验证。

针对这种情况,我们也可以自定义一个注解来完成参数的校验,也便于进一步了解注解验证的原理。

自定义注解验证,实现方式如下!

首先,创建一个Sex注解。

@Target({FIELD})
@Retention(RUNTIME)
@Constraint(validatedBy = SexValidator.class)
@Documented
public @interface Sex {

    String message() default "性别值不在可选范围内";

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

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

然后,创建一个SexValidator类,实现自ConstraintValidator接口

public class SexValidator implements ConstraintValidator<Sex, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        Set<String> sexSet = new HashSet<String>();
        sexSet.add("男");
        sexSet.add("女");
        return sexSet.contains(value);
    }
}

最后,在User实体类上加入一个性别参数,使用自定义注解进行校验!

public class User {

    @NotBlank(message = "用户名不能为空!")
    private String userName;

    @Email(message = "邮箱格式不正确")
    @NotBlank(message = "邮箱不能为空!")
    private String email;

    @NotBlank(message = "密码不能为空!")
    @Size(min = 8, max = 16,message = "请输入长度在8~16位的密码")
    private String userPwd;

    /**
     * 自定义注解校验
     */
    @Sex(message = "性别输入有误!")
    private String sex;

    // set、get方法等...
}

启动服务,重新请求,运行结果如下:

结果与预期一致!

五、总结

参数验证,在开发中使用非常频繁,如何优雅的进行验证,让代码变得更加可读,是业界大佬一直在追求的目标!

本文主要围绕在 Spring Boot 中实现参数统一验证进行相关的知识总结和介绍,如果有描述不对的地方,欢迎留言支持。

示例代码:spring-boot-example-valid

From:https://www.cnblogs.com/dxflqm/p/18320328
本文地址: http://shuzixingkong.net/article/364
0评论
提交 加载更多评论
其他文章 全球电脑蓝屏崩溃,为何中国没事?周鸿祎:因 90% 用 360 软件!
导致这次故障的罪魁祸首正是全球知名的安全软件公司 Crowdstrike,它在全球范围内被广泛用于管理 Windows PC 和服务器的安全。
全球电脑蓝屏崩溃,为何中国没事?周鸿祎:因 90% 用 360 软件! 全球电脑蓝屏崩溃,为何中国没事?周鸿祎:因 90% 用 360 软件! 全球电脑蓝屏崩溃,为何中国没事?周鸿祎:因 90% 用 360 软件!
【译】宣布三项新的高级 Visual Studio 订阅者福利
Visual Studio 订阅(无论是专业版还是企业版)提供的不仅仅是软件使用权;这是一个全面的工具包,旨在显著提高您的开发能力和职业发展。这些订阅每年可以为您节省数千美元,提供各种服务,从每月用于云实验的 Azure 积分,到免费访问 Pluralsight 和 LinkedIn Learnin
【译】宣布三项新的高级 Visual Studio 订阅者福利 【译】宣布三项新的高级 Visual Studio 订阅者福利
云端IDE如何重定义开发体验
总的来说,豆包 MarsCode 不仅是一个工具,更是现代软件开发中不可或缺的助手。它的出现不仅推动了开发工具的技术进步,更为开发者们带来了全新的开发体验和工作方式。期待未来豆包 MarsCode 在AI驱动开发领域继续创新,为全球开发者提供更多实用且强大的功能。
云端IDE如何重定义开发体验 云端IDE如何重定义开发体验 云端IDE如何重定义开发体验
辅助分类器生成对抗网络( Auxiliary Classifier Generative Adversarial Network,ACGAN)(附带pytorch代码)
ACGAN相对于CGAN使的判别器不仅可以判别真假,也可以判别类别 。通过对生成数据类别的判断,判别器可以更好地传递loss函数使得生成器能够更加准确地找到label对应的噪声分布。
辅助分类器生成对抗网络( Auxiliary Classifier Generative Adversarial Network,ACGAN)(附带pytorch代码) 辅助分类器生成对抗网络( Auxiliary Classifier Generative Adversarial Network,ACGAN)(附带pytorch代码) 辅助分类器生成对抗网络( Auxiliary Classifier Generative Adversarial Network,ACGAN)(附带pytorch代码)
.NET 中高效 Excel 解决方案 MiniExcel
前言 MiniExcel 是一个用于 .NET 平台的轻量级、高性能的库,专注于提供简单易用的 API 来处理 Excel 文件。以下是 MiniExcel 的特点总结: 轻量级与高效:MiniExcel 设计为占用较少的系统资源,尤其在内存使用上表现优秀,适合处理大数据集而不会导致内存溢出。 简单
.NET 中高效 Excel 解决方案 MiniExcel .NET 中高效 Excel 解决方案 MiniExcel .NET 中高效 Excel 解决方案 MiniExcel
无需任何软件,教你手动三个步骤即可修改远程桌面3389端口!
远程桌面默认的端口是3389端口,远程桌面是个人电脑与windows服务器的链接桥梁。每一台服务器使用默认的远程桌面端口3389都逃不过被黑客扫描和爆破的命运!windows的远程桌面端口是在注册表中控制的,所以我们修改远程桌面端口,实际上修改注册表信息即可
无需任何软件,教你手动三个步骤即可修改远程桌面3389端口! 无需任何软件,教你手动三个步骤即可修改远程桌面3389端口! 无需任何软件,教你手动三个步骤即可修改远程桌面3389端口!
fasterWhisper和MoneyPrinterPlus无缝集成
fasterWhisper是一款优秀的语音识别工具,现在它可以和MoneyPrinterPlus无缝集成了。
fasterWhisper和MoneyPrinterPlus无缝集成 fasterWhisper和MoneyPrinterPlus无缝集成 fasterWhisper和MoneyPrinterPlus无缝集成
面试常考--前端性能优化之大文件上传
大文件上传是前端开发中常见的需求之一,特别是在需要处理高清图片、视频或其他大型文件时。优化大文件上传不仅可以提升用户体验,还能有效减轻服务器负担。本文将深入探讨大文件上传的几种常见优化技术,包括文件切片与并发上传、断点续传、后台处理优化、安全性考虑和用户体验优化。 1. 前言 在现代Web应用中,用
面试常考--前端性能优化之大文件上传