Java使用validation拦截非法提交的数据的方法实现
目录
- 一.引入依赖
- 二.提交数据的实体类添加验证规则的注解
- 三.在控制器方法上添加@Valid注解
- 拓展:分组校验
- 1. 默认行为
- 2. 示例分析
- 3. 解决方案
- 方案1:在Controller类上添加@Validated注解
- 方案2:在方法参数上指定分组
- 方案3:混合使用(推荐)
- 4. 重要注意事项
- 5. 使用建议
注意:本文使用的是Springboot 2.x版本,如果使用其他版本会略有差异,本文仅作抛转引义,开发时需结合自己项目的实际情况来定。
一.引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
二.提交数据的实体类添加验证规则的注解
需根据属性的类型和业务规则添加相应的注解
import lombok.Data; import org.hibernate.validator.constraints.URL; import Javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; /** * @author: * @Desc: * @create: 2025-04-14 10:32 **/ @Data public class StoreDeviceDTO { @NotNull(message = "storeId不能为空") private Long storeId; @NotBlank(message = "设备名称不能为空") private String deviceName; @NotBlank(message = "设备图片不能为空") @URL(message = "设备图片必须是一个合法的URL地址") private String deviceImage; }
三.在控制器方法上添加@Valid注解
@PostMapping("/addApply") public AJAXResult add(@Valid @RequestBody StoreApplyDTO storeApplyDTO) { storeApplyService.saveStoreApply(storeApplyDTO); return AjaxResult.success(); }
如果传的集合类型的数据,则比较特殊,需要在控制器类名上加上@Validated注解,同时方法中也要加上上@Validated注解,正确的使用方式如下:
@RestController @Validated public class StoreDeviceController { @Autowired private IStoreDeviceServic编程客栈e storeDevicesService; @PostMapping("/saveDevices") public AjaxResult saveDevices(@RequestBody @Validated List<@Valid StoreDeviceDTO> deviceDTOList) { storeDevicesService.saveDevices(deviceDTOList); return AjaxResult.success(); } }
其中@Valid和@Validated引用的包为:
import org.springframework.validation.annotation.Validated; import javax.validation.Valid;
为了给前端返回时是可读性强的文字说明,而不是一堆英文说明,需要在全局异常处理类中添加如下代码:
@RestControllerAdvice public class GlobalExceptionHandler { /** * 自定www.devze.com义验证异常 */ @ExceptionHandler(MethodArgumentNotValidException.class) public Object handleMethodArgumentNotVjavascriptalidException(MethodArgumentNotValidException e) { //log.error(e.getMessage(), e); String message = e.getBindingResult().getFieldError().getDefaultMessage(); return AjaxResult.error(message); } }
第一个返回示例:
{ "msg": "门店id不能为空", "code": 500 }
第二个返回示例(可进一步优化):
{ "msg": "saveDevices.deviceDTOList[0].deviceName: 设备名称不能为空, saveDevices.deviceDTOList[0].deviceImage: 设备图片必须是一个合法的URL地址", "code": 500 }
拓展:分组校验
很多时候,有新增和修改操作,需进行不同的验证,比如新增时无需带id,而修改时必须带id,此时可通过分组检验,以实现区别对接。下面是实现的过程:
1.创建新建和修改的分组interface
//新增 public interface AddGroup { } //修改 public interface UpdateGroup { }
2.请求体添加分组注解
import com.bbc.validate.group.AddGroup; import com.bbc.validate.group.UpdateGroup; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; import javax.validation.constraints.*; import java.util.List; /** * @author: * @Desc: * @create: 2025-03-22 14:34 **/ @Data @ApiModel(description = "认证项目请求体") public class CertificateDTO { @ApiModelProperty("申请认证ID") @NotNull(message = "申请认证ID不能为空",groups ={UpdateGroup.class}) private Long id; @NotNull(message = "storeId不能为空",groups ={AddGroup.class,UpdateGroup.class}) private Long storeId; @NotBlank(message = "认证项目不能为空",groups ={AddGroup.class,UpdateGroup.class}) @ApiModelProperty("认证项目") private String project; @NotNull(message = "请上传证书材料", groups = {AddGroup.class}) @Size(min = 1, max = 9, message = "请上传1-9张证书材料图片", groups = {AddGroup.class, UpdateGroup.class})php @ApiModelProperty("认证项目材料图片(1-9张)") private List<String> certificateMaterials; @Size(max = 200, message = "认证说明最多200个字符") @ApiModelProperty("认证说明/备注(最多200字)") private String instructions; }
3.controller中的方法添加了分组
@ApiOperation("新增认证管理") @Log(title = "认证管理", businessType = BusinessType.INSERT) @PostMapping("/saveProject") @RepeatSubmit(interval = 5000, message = "不允许重复提交,请稍候再试") public AjaxResult add(@Validated(AddGroup.class) @RequestBody CertificateDTO certificateDTO) { certificateService.saveCertificateProject(certificateDTO); return AjaxResult.success(); } /** * 修改认证管理 */ @ApiOperation("修改认证管理") @Log(title = "认证管理", businessType = BusinessType.UPDATE) @PutMapping("/editProject") public AjaxResult edit(@Validated(UpdateGroup.class) @RequestBody CertificateDTO certicateDTO) { certificateService.updateCertificateProject(certicateDTO); return AjaxResult.success(); }
避坑指南:
如果DTO类某个属性添加了分组校验,但Controller方法未指定校验分组,会导致分组校验失效,没有添加分组注解的基础校验仍然有效。
情况说明:
1. 默认行为
- 未指定分组:当Controller方法没有使用
@Validated
指定分组时,只会校验没有分组的约束注解 - 有分组注解:带有
groups
属性的校验注解将被跳过(因为没有添加@Validated注解
)
2. 示例分析
public class CertificateDTO { @NotEmpty(message = "证书材料不能为空", groups = {AddGroup.class}) // 有分组 private List<String> certificateMaterials; @Size(max = 200) // 无分组 private String instructions; }
在Controller中:
@PostMapping public void save(@RequestBody @Valid StoreCertificateDTO dto) { // 只会校验instructions字段 // certificateMaterials不会被校验,因为未指定AddGroup分组 }
3. 解决方案
方案1:在Controller类上添加@Validated注解
@RestController @Validated(AddGroup.class) // 指定默认分组 public class CertificateController { @PostMapping public void save(@RequestBody @Valid CertificateDTO dto) { // 现在会校验AddGroup分组和没有分组的约束 } }
方案2:在方法参数上指定分组
@PostMapping public void save(@RequestBody @Validated(AddGroup.class) StoreCertificateDTO dto) { // 明确指定校验分组 }
方案3:混合使用(推荐)
@RestController @Validated // 类级别启用校验 public class CertificateController { @PostMapping("/add") public void add(@RequestBody @Validated(AddGroup.class) StoreCertificateDTO dto) { // 添加操作使用AddGroup分组 } jQBamO @PostMapping("/update") public void update(@RequestBody @Validated(UpdateGroup.class) StoreCertificateDTO dto) { // 更新操作使用UpdateGroup分组 } }
4. 重要注意事项
@Valid
vs @Validated
:
@Valid
(jsR-303)不支持分组@Validated
(Spring扩展)支持分组
默认校验:
- 没有分组的约束注解总是会被校验
- 有分组的约束只在指定对应分组时才会校验
继承关系:
- 分组可以继承,父接口的分组会被子接口继承
分组虽好,使用不当可能会得不到预期的结果,那么什么情况下要分组,什么情况下不分组呢:
5. 使用建议
- 明确指定分组:重要的业务校验都应该明确指定分组
- 分层设计:
- 基础校验(非空、格式等)可以不分组
- 业务规则校验使用分组
- 文档注释:在代码中注明各分组的用途
- 测试覆盖:编写测试验证分组校验是否按预期工作
到此这篇关于Java使用validation拦截非法提交的数据的文章就介绍到这了,更多相关Java使用validation拦截非法提交内容请搜索编程客栈(www.devze.com)以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程客栈(www.devze.com)!
精彩评论