springboot中使用Hibernate-Validation

1、說明

后端接口設計時候,需要對前端請求參數進行'先校驗后處理業(yè)務'情況,如果在業(yè)務代碼中通過類似if這里逐個校驗,會使得代碼變得繁瑣,開發(fā)工作者都是愛偷懶的。java中,Bean ValidationJavaBean的驗證定義了相關的元數據模型和API?;?code>Bean-Validation封裝,提供了更加豐富的Hibernate-Validation的校驗包。也有開發(fā)會把這類校驗交給前端來處理,但是接口暴露外網會存在直接調用情況(黃牛)。畢竟:前端校驗是為了提高用戶的體驗度,后端校驗則是為了保證數據的安全性

優(yōu)點
1.驗證邏輯與業(yè)務邏輯之間進行了分離,降低了程序耦合度
2.統(tǒng)一且規(guī)范的驗證方式,無需你再次編寫重復的驗證代碼
3.你將更專注于你的業(yè)務,將這些繁瑣的事情統(tǒng)統(tǒng)丟在一邊

2、Bean Validation與Hibernate Validation

2.1 Bean Validation中內置的constraint

包位置路徑:javax.validation.constraints

image.png

注解 說明
@AssertFalse 注釋的元素必須為False
@AssertTrue 注釋的元素必須為True
@Email 注釋的元素必須郵箱
@NotBlank 注釋的元素不能為空,!null && size>0
@NotEmpty 注釋的元素不能為空,數組,集合等
@NotNull 注釋的元素必須為空,但可以為""字符串
@DecimalMin 注釋的元素數字,最小不得小于Min
@DecimalMax 注釋的元素為數字,最大不超過Max值

其中NotNull、NotEmpty、NotBlank區(qū)別

  • @NotNull
    適用于基本數據類型(Integer,Long,Double等等),當 @NotNull 注解被使用在 String 類型的數據上,則表示該數據不能為 Null(但是可以為 Empty)
  • @NotBlank
    適用于 String 類型的數據上,加了@NotBlank 注解的參數不能為 Null 且 trim() 之后 size > 0
  • @NotEmpty
    適用于 String、Collection集合、Map、數組等等,加了@NotEmpty 注解的參數不能為 Null 或者 長度為 0
2.1 Hibernate Validation中添加的constraint
image.png
注解 說明
@Length 注釋的元素字符串長度必須為制定返回內
@Range 注釋的元素必須在指定范圍內
@URL 注釋的元素必須為鏈接

3、基于Hibernate Validation的實現

(1)pom包引用
查看spring-boot-start-web中已經集成了Hibernate Validation,所以可以不用額外引用包。同時spring-boot-start-validation也完成了Hibernate Validationstart封裝(校驗機制更加全面)。

 <dependency>
        <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-validation</artifactId>
       <version>2.2.6.RELEASE</version>
</dependency>

(2)Bean對象中使用注解注釋

     ...
    @ApiModelProperty(value = "收貨人所在省",required = true)
    @NotNull(message = "省不能為空")
    private String recipientProvince;

    @ApiModelProperty(value = "收貨人所在市")
    @NotNull(message = "市不能為空")
    private String recipientCity;

    @ApiModelProperty(value = "收貨人所在區(qū)")
    @NotNull(message = "區(qū)不能為空")
    private String recipientDistrict;
    ...

(3)Controller層使用@Valid或者@Validated

 @PostMapping("/add")
 public UniformResultTemplate<Boolean> addAddress(@RequestBody @Validated AddressReqDto reqDto, HttpServletRequest request){
   return null;
 }

注意:Post請求方式區(qū)別,Get@Validated注解需要加在 所在方法類前

@RestController
@RequestMapping("/api/address")
@Validated
public class AddressController extends BaseController{

  @ApiOperation("收獲地址詳情")
    @GetMapping("/detail")
    public UniformResultTemplate<AddressDetailRespDto> queryAddressList(@NotNull(message = "地址Id不能為空") 
@RequestParam(value = "addressId") Long addressId, HttpServletRequest request){
        return null;
    }
}

(4)使用@ControllerAdvice統(tǒng)一異常處理返回。

@Component
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
    // Post請求Bean中的校驗拋出:MethodArgumentNotValidException
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseBody
    public UniformResultTemplate handleBindException(MethodArgumentNotValidException ex) {
        FieldError fieldError = ex.getBindingResult().getFieldError();
        log.warn("參數校驗異常:{}({})", fieldError.getDefaultMessage(),fieldError.getField());
        return new UniformResultTemplate("10002",fieldError.getDefaultMessage());
    }

  // Get請求的參數校驗,拋出的是ConstraintViolationException
  @ExceptionHandler(ConstraintViolationException.class)
    @ResponseBody
    public UniformResultTemplate handleGetBindException(ConstraintViolationException ex) {
        Set<ConstraintViolation<?>>  eSet = ex.getConstraintViolations();
        StringBuffer sb = new StringBuffer();
        if(!CollectionUtils.isEmpty(eSet)) {
            Iterator<ConstraintViolation<?>> iterator = eSet.iterator();
            while (iterator.hasNext()) {
                log.warn("參數校驗異常:{}({})", iterator.next().getMessage());
                sb.append(iterator.next().getMessage()).append("::");
            }
        }
        return new UniformResultTemplate("10002",sb.toString());
    }

  // 方法簽名參數錯誤
    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseBody
    public UniformResultTemplate handleGetBindException(MissingServletRequestParameterException ex) {
        log.warn("參數校驗異常:{}", ex.getMessage());
        return new UniformResultTemplate("10002",ex.getMessage());
    }
}

(5)結果現象

{
    "code": "10002",
    "message": "市不能為空",
    "result": null,
    "totalTimes": null,
    "interfaceTimes": null
}

4、編譯器校驗工具

防止因使用錯誤Hibernate-Validation注解而導致程序運行時報錯,增加編譯器校驗工具,進行友好提示。

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator-annotation-processor</artifactId>
    <version>6.1.5.Final</version>
</dependency>
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容