Springboot接口參數校驗

在設計接口時我們通常需要對接口中的非法參數做校驗,以降低在程序運行時因為一些非法參數而導致程序發生異常的風險,例如登錄的時候需要校驗用戶名密碼是否為空,創建用戶的時候需要校驗郵件、手機號碼格式是否準確。如果在代碼中對接口參數一個個硬編碼校驗的話就太繁瑣了,代碼可讀性極差。這時不妨試試Validator框架。

原生的依賴是validation-api而hibernate-validator是對validation-api的增強,hibernate-validator框架已經集成在spring-boot-starter-web中。

<dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>5.2.0.Final</version>
</dependency>

但從springboot-2.3開始,校驗包被獨立成了一個starter組件,所以需要引入validation和web,而springboot-2.3之前的版本只需要引入web依賴就可以了。?

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

部分常見注解如下:

注解功能
@AssertFalse可以為null,如果不為null的話必須為false
@AssertTrue可以為null,如果不為null的話必須為true
@DecimalMax設置不能超過最大值
@DecimalMin設置不能超過最小值
@Digits設置必須是數字且數字整數的位數和小數的位數必須在指定范圍內
@Future日期必須在當前日期的未來
@Past日期必須在當前日期的過去
@Max最大不得超過此最大值
@Min最大不得小于此最小值
@NotNull不能為null,可以是空
@Null必須為null
@Pattern必須滿足指定的正則表達式
@Size集合、數組、map等的size()值必須在指定范圍內
@Email必須是email格式
@Length長度必須在指定范圍內
@NotBlank字符串不能為null,字符串trim()后也不能等于“”
@NotEmpty不能為null,集合、數組、map等size()不能為0;字符串trim()后可以等于“”
@Range值必須在指定范圍內
@URL必須是一個URL

一、基礎注解使用?

注解簡單演示類

import lombok.Data;
import org.hibernate.validator.constraints.Length;import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;@Data
public class UserVo {@Length(min = 4, max = 12, message = "賬號長度必須位于4到10之間")private String userCode;@NotBlank(message = "用戶名不能為空")private String userName;@Email(message = "郵箱格式有誤")private String email;@Pattern(regexp = "^1[3456789]\\d{9}$", message = "手機號格式錯誤")private String phone;/*** 0:普通用戶;VIP:會員用戶;2:超級VIP用戶*/@Pattern(regexp = "[012]", message = "用戶類型有誤,請輸入0:普通用戶;VIP:會員用戶;2:超級VIP用戶")private String type;
}

在全局異常處理里面加上對參數校驗異常的攔截

    @ExceptionHandler(value = {BindException.class, ValidationException.class, MethodArgumentNotValidException.class})public ResponseVo validatedExceptionHandler(Exception e) {log.error("參數校驗異常!原因是{}", e);if (e instanceof MethodArgumentNotValidException) {//BeanValidation exceptionMethodArgumentNotValidException ex = (MethodArgumentNotValidException) e;return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(),ex.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining("; ")));} else if (e instanceof ConstraintViolationException) {//BeanValidation GET simple paramConstraintViolationException ex = (ConstraintViolationException) e;return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(),ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining("; ")));} else if (e instanceof BindException) {//BeanValidation GET object paramBindException ex = (BindException) e;return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(),ex.getAllErrors().stream().map(ObjectError::getDefaultMessage).collect(Collectors.joining("; ")));}return ResponseVo.error(ResponseEnum.BODY_NOT_MATCH.getResultCode(), ResponseEnum.BODY_NOT_MATCH.getResultMsg());}

?定義參數校驗的controller

import com.yx.light.element.mybatis.vo.UserVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.validation.constraints.Email;@RestController
@RequestMapping(value = "/valid")
@Validated
@Slf4j
public class ValidController {/*** 單參數校驗,單參數需要在controller上加上@Validated配合使用* @param email* @return*/@PostMapping(value = "/oneParam", produces = "application/json; charset=UTF-8")public String oneParam(@Email(message = "郵箱格式有誤") String email) {log.info("校驗郵箱為:{}", email);return "郵箱校驗成功";}/*** body類型校驗* @param vo* @return*/@PostMapping(value = "/bodyParam", produces = "application/json; charset=UTF-8")public String bodyParam(@Validated @RequestBody UserVo vo) {log.info("校驗body為:{}", vo);return "body校驗成功";}/*** 表單參數校驗* @param vo* @return*/@PostMapping(value = "/formParam", produces = "application/json; charset=UTF-8")public String formParam(@Validated UserVo vo) {log.info("校驗表單為:{}", vo);return "表單校驗成功";}
}

oneParam接口

bodyParam接口

formParam接口

?二、自定義參數校驗

當然你也可以自定義參數校驗,出現有些復雜的邏輯基本注解是沒辦法兼容的,需要我們自己去實現自動校驗。

自定義校驗注解

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;@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 {};
}

?想讓自定義驗證注解生效,需要實現ConstraintValidator接口。接口的第一個參數是 自定義注解類型,第二個參數是 被注解字段的類。

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.lang.annotation.Annotation;
import java.util.function.Predicate;@Slf4j
public class UserValidation<T extends Annotation> implements ConstraintValidator<T, UserVo> {protected Predicate<UserVo> predicate = c -> true;@Overridepublic boolean isValid(UserVo user, ConstraintValidatorContext constraintValidatorContext) {return predicate.test(user);}/*** 校驗用戶是否唯一* 即判斷數據庫是否存在當前新用戶的信息,如用戶編碼,手機,郵箱*/public static class UniqueUserValidator extends UserValidation<UniqueUser> {@Overridepublic void initialize(UniqueUser uniqueUser) {predicate = c -> !existsUser(c.getUserCode(), c.getEmail(), c.getPhone());}private boolean existsUser(String userCode, String email, String phone) {//TODO 寫一個數據庫查詢判斷是否存在相同的用戶,省略...return true;}}
}

在controller中添加測試接口

@PostMapping(value = "/addUser", produces = "application/json; charset=UTF-8")
public String addUser(@UniqueUser @Validated @RequestBody UserVo vo) {log.info("校驗body為:{}", vo);return "新增成功";
}

addUser接口?

?三、分組校驗

如果你的一個實體中的字段某一些是新增的時候必傳,某一些修改時又不用傳,那么對于不用傳的字段肯定不需要校驗的,這時候如果我們共用一個實體作為多個接口參數那肯定存在兼容問題,此時你就可以考慮將參數分組判斷。

定義一個分組接口ValidGroup讓其繼承javax.validation.groups.Default,再在分組接口中定義出多個不同的操作類型,Create,Update,Query,Delete。

import javax.validation.groups.Default;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 {}}
}

稍微修改一下原來的vo,給他們加上分組參數groups

import com.yx.light.element.mybatis.validation.ValidGroup;
import lombok.Data;
import org.hibernate.validator.constraints.Length;import javax.validation.constraints.Email;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Pattern;@Data
public class UserVo {@Length(min = 4, max = 12, message = "賬號長度必須位于4到10之間", groups = ValidGroup.Crud.Update.class)private String userCode;@NotBlank(message = "用戶名不能為空", groups = ValidGroup.Crud.Create.class)private String userName;@Email(message = "郵箱格式有誤")private String email;@Pattern(regexp = "^1[3456789]\\d{9}$", message = "手機號格式錯誤")private String phone;/*** 0:普通用戶;VIP:會員用戶;2:超級VIP用戶*/@Pattern(regexp = "[012]", message = "用戶類型有誤,請輸入0:普通用戶;VIP:會員用戶;2:超級VIP用戶")private String type;
}

在controller中添加如下方法,這里我們通過value屬性給addUserV2()和updateUser()方法分別指定Create和Update分組。

@PostMapping(value = "/addUserV2", produces = "application/json; charset=UTF-8")
public String addUserV2(@Validated(value = ValidGroup.Crud.Create.class) @RequestBody UserVo vo) {log.info("校驗body為:{}", vo);return "新增成功";
}@PostMapping(value = "/updateUser", produces = "application/json; charset=UTF-8")
public String updateUser(@Validated(value = ValidGroup.Crud.Update.class) @RequestBody UserVo vo) {log.info("校驗body為:{}", vo);return "更新成功";
}

addUserV2接口

updateUser接口

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/711820.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/711820.shtml
英文地址,請注明出處:http://en.pswp.cn/news/711820.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

系統集成Prometheus+Grafana

根據產品需求在自己的系統中添加一個系統監控的頁面&#xff0c;其中有主機信息的顯示&#xff0c;也有一些業務信息的顯示。調研后的方案是 主機信息通過Prometheus采集和存儲&#xff0c;業務信息通過自己系統的調度任務統計后存儲在Mysql中&#xff0c;使用Grafana對接Prome…

Java必須掌握的繼承的特點和繼承體系的設計(含面試大廠題和源碼)

Java繼承是面向對象編程的一個基本特性&#xff0c;它允許一個類繼承另一個類的屬性和方法。設計良好的繼承體系是高質量軟件開發的關鍵。在大廠面試中&#xff0c;面試官可能會詢問關于Java繼承特點及如何設計一個合理的繼承體系的問題&#xff0c;以評估你的面向對象設計能力…

ICLR 2024|ReLU激活函數的反擊,稀疏性仍然是提升LLM效率的利器

論文題目&#xff1a; ReLU Strikes Back: Exploiting Activation Sparsity in Large Language Models 論文鏈接&#xff1a; https://arxiv.org/abs/2310.04564 參數規模超過十億&#xff08;1B&#xff09;的大型語言模型&#xff08;LLM&#xff09;已經徹底改變了現階段人工…

gcc和g++的區別,如何看自己的編譯器支持的C++的版本

gcc和g的區別 用一句話來說&#xff0c;就是gcc將程序視為c語言的&#xff0c;g將程序視為C的 gcc和g的區別主要在于它們處理不同后綴的文件類型、編譯和連接階段的不同調用方式&#xff0c;以及它們對C特性的支持方式。以下是詳細介紹&#xff1a;123 文件類型。gcc將后綴為…

通過多線程并發方式實現服務器

與多進程實現對比來看。 示例來源于網絡視頻 #include <stdio.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> #include <ctype.h> #include <unistd.h> #include <fcntl.h>#include "wrap.h"#de…

【C++ 測試】

C 測試 一、二維數組二、私有成員三、function用法四、類里面創建另一個類五、lambda六、Map動態申請 一、二維數組 #include <iostream> #include<windows.h> #include <map> // SetConsoleOutputCP ( CP_UTF8 ) ; using namespace std;void test1() {map…

求最短路徑之迪杰斯特拉算法

對fill用法的介紹 1.用鄰接矩陣實現 const int maxn100; const int INF100000000;//無窮大&#xff0c;用來初始化邊 int G[maxn][maxn];//用鄰接矩陣存儲圖的信息 int isin[maxn]{false};//記錄是否已被訪問 int minDis[maxn];//記錄到頂點的最小距離void Dijkstra(int s,in…

網格圖的搜索

來自靈神網格圖題單。 1. 網格圖 1.1. LC 200 島嶼數量 這題我一開始想繁了&#xff0c;想維護并查集&#xff0c;然后看等價類個數。其實完全沒有必要。因為連通分量深搜到頭就可以直接給答案計數1。利用vis數組維護訪問過的點&#xff0c;然后碰到新連通分量重新深搜即可。…

Pinia使用

官方地址&#xff1a;Pinia | The intuitive store for Vue.js (vuejs.org)https://pinia.vuejs.org/ 1.安裝 npm install pinia npm install pinia-plugin-persistedstate Pinia是一個基于Vue 3的狀態管理庫&#xff0c;它使得管理Vue的全局狀態變得更加容易和直觀。 而…

自定義el-dialog的樣式

實現效果&#xff1a; 樣式代碼如下&#xff1a;&#xff08;可以寫在common.scss文件夾中&#xff09; .el-dialog__header {padding: 16px 20px;border-bottom: 1px solid #DCDFE6;display: flex;align-items: center;.el-dialog__title {font-size: 16px;position: relativ…

utniy urp shinyssrr插件使用

文章目錄 前言步驟1首先在URP的配置文件里添加SSR后處理2 修改RenderingPath為延遲渲染3 啟用深度紋理4 為物體添加腳本 插件下載 前言 用來實現屏幕空間反射效果 unity 版本為2021.3.8LTS&#xff0c;低版本的untiy URP的參數設置位置z可能會不同 步驟 1首先在URP的配置文件…

記錄阿里云換源失敗的慘痛教訓

聲明 首先我不是一個云服務器小白&#xff0c;但是之前一直在使用騰訊云和火山引擎的云服務器。從未見過阿里云這樣如此**的運營商。 問題 服務器到手&#xff0c;第一步在我進行sudo apt update的時候&#xff0c;也就是更新軟件包的時候&#xff0c;我發現&#xff0c;一直…

1028. 從先序遍歷還原二叉樹(三種方法:棧+遞歸+集合)

文章目錄 1028. 從先序遍歷還原二叉樹&#xff08;三種方法&#xff1a;棧遞歸集合&#xff09;一、棧 while迭代1.思路2.代碼 二、遞歸法1.思路2.代碼 三、集合存儲1.思路2.代碼 1028. 從先序遍歷還原二叉樹&#xff08;三種方法&#xff1a;棧遞歸集合&#xff09; 一、棧 wh…

hive報錯:FAILED: NullPointerException null

發現問題 起因是我虛擬機的hive不管執行什么命令都報空指針異常的錯誤 我也在網上找了很多相關問題的資料&#xff0c;發現都不是我這個問題的解決方法&#xff0c;后來在hive官網上與hive 3.1.3版本相匹配的hadoop版本是3.x的版本&#xff0c;而我的hadoop版本還是2.7.2的版本…

HTTPS的加密過程

文章目錄 前言一、為什么需要加密&#xff1f;二、只用對稱加密可以嗎&#xff1f;三、只使用非對稱加密四、雙方都使用非對稱加密五、使用非對稱加密對稱加密六、引入證書1.如何放防止數字證書被篡改&#xff1f;2.中間人有可能篡改該證書嗎&#xff1f;3.中間人有可能掉包該證…

開窗函數rank() over,dense_rank() over,row_number() over的區別

1.rank() over 查詢出指定的條件進行排名&#xff0c;條件相同排名相同的話&#xff0c;排名之間是不連續的 例如排名如 1 2 3 3 5 6 7 等&#xff0c;相同的排名會自動跳過 2.dense_rank() over 查詢出指定的條件后進行排名&#xff0c;條件相同&#xff0c;排名相同的話&…

【YOLO系列】YOLOv9論文超詳細解讀(翻譯 +學習筆記)

前言 時隔一年&#xff0c;YOLOv8還沒捂熱&#xff0c;YOLO系列最新版本——YOLOv9 終于閃亮登場&#xff01; YOLOv9的一作和v7一樣。v4也有他。 他于2017年獲得臺灣省National Central University計算機科學與信息工程博士學位&#xff0c;現在就職于該省Academia Sinica的…

【大數據】Flink SQL 語法篇(六):Temporal Join

《Flink SQL 語法篇》系列&#xff0c;共包含以下 10 篇文章&#xff1a; Flink SQL 語法篇&#xff08;一&#xff09;&#xff1a;CREATEFlink SQL 語法篇&#xff08;二&#xff09;&#xff1a;WITH、SELECT & WHERE、SELECT DISTINCTFlink SQL 語法篇&#xff08;三&…

機器視覺——硬件選型

1、相機選型 在選擇機器視覺相機時&#xff0c;通常需要考慮以下幾個方面&#xff1a; 1、分辨率&#xff1a;相機的分辨率決定了其拍攝圖像的清晰度和細節程度。根據具體的應用需求&#xff0c;可以選擇適當的分辨率范圍。 2、幀率&#xff1a;幀率表示相機每秒鐘能夠拍攝的…

2023年營養保健品線上電商市場行業分析(2024年營養保健行業未來趨勢分析)

近年來&#xff0c;受人口老齡化、養生年輕化等因素驅動&#xff0c;保健品行業增長強勁&#xff0c;加之越來越多的年輕人也加入養生大軍&#xff0c;成為保健品市場上的一股新力量&#xff0c;進一步帶動市場擴容。 鯨參謀數據顯示&#xff0c;2023年度&#xff0c;京東平臺…