Spring Boot中使用JSR-303實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn)
JSR-303是Java中的一個(gè)規(guī)范,用于實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn)。它定義了一組注解,可以應(yīng)用于JavaBean的字段上,用于驗(yàn)證輸入?yún)?shù)的合法性。下面是一些常用的JSR-303注解及其介紹:
@NotNull:用于驗(yàn)證字段值不能為null。@NotEmpty:用于驗(yàn)證字符串字段不能為空。@NotBlank:用于驗(yàn)證字符串字段不能為空,并且長(zhǎng)度必須大于0。@Min:用于驗(yàn)證數(shù)字字段的最小值。@Max:用于驗(yàn)證數(shù)字字段的最大值。@Size:用于驗(yàn)證字符串、集合或數(shù)組字段的長(zhǎng)度或大小。@Pattern:用于驗(yàn)證字符串字段是否匹配指定的正則表達(dá)式。@Email:用于驗(yàn)證字符串字段是否符合Email格式。@Range:用于驗(yàn)證數(shù)字字段的取值范圍。@Valid:用于嵌套驗(yàn)證,可以對(duì)對(duì)象中的字段進(jìn)行遞歸驗(yàn)證。
其他如下圖所示:

通過(guò)在JavaBean的字段上添加這些注解,可以在接收請(qǐng)求參數(shù)時(shí)進(jìn)行自動(dòng)校驗(yàn)。如果校驗(yàn)失敗,可以通過(guò)異常處理機(jī)制來(lái)處理校驗(yàn)錯(cuò)誤。
需要注意的是,JSR-303只提供了基本的驗(yàn)證注解,如果需要更復(fù)雜的校驗(yàn)邏輯,可以自定義注解或使用第三方庫(kù),如Hibernate Validator等。
Hibernate Validator附加的constraint:

至于我們?yōu)樯妒褂肑SR-303來(lái)校驗(yàn)我們的參數(shù),主要是為了解決我們?cè)趯?shí)際開發(fā)過(guò)程中,前后端的參數(shù)校驗(yàn)導(dǎo)致的問(wèn)題。比如:
- 我們依靠前端框架解決參數(shù)校驗(yàn),但是缺少服務(wù)器的參數(shù)校驗(yàn),這種情況常見于需要同時(shí)進(jìn)行開發(fā)前后端的時(shí)候,雖然程序的正常使用不會(huì)有問(wèn)題,到那時(shí)開發(fā)者會(huì)忽略非正常的操作,比如繞過(guò)前端程序,直接模擬客戶端請(qǐng)求,這樣就繞過(guò)了我們對(duì)前端預(yù)設(shè)的各種限制,直擊我們后端的數(shù)據(jù)訪問(wèn)接口,進(jìn)而使得我們的后端系統(tǒng)存在嚴(yán)重的安全隱患。
- 大量的利用if/else語(yǔ)句嵌套實(shí)現(xiàn),使得我們的校驗(yàn)邏輯晦澀難懂,并不利于我們對(duì)整個(gè)系統(tǒng)的維護(hù)。
JSR的定義標(biāo)準(zhǔn)
驗(yàn)證觸發(fā)時(shí)機(jī):
- JSR-303校驗(yàn)標(biāo)準(zhǔn)定義了兩個(gè)觸發(fā)校驗(yàn)的時(shí)機(jī):
- 在對(duì)象被持久化之前(例如,保存到數(shù)據(jù)庫(kù)之前)。
- 在對(duì)象被修改之后(例如,更新數(shù)據(jù)庫(kù)中的記錄后)。
- 校驗(yàn)約束注解:JSR-303標(biāo)準(zhǔn)提供了一組注解,可以用于對(duì)Java對(duì)象的屬性進(jìn)行校驗(yàn)。這些注解包括但不限于
@NotNull、@NotEmpty、@Min、@Max、@Size、@Pattern、@Email等。開發(fā)人員可以根據(jù)需求選擇適當(dāng)?shù)淖⒔鈦?lái)定義校驗(yàn)規(guī)則。 - 校驗(yàn)組:JSR-303允許將校驗(yàn)規(guī)則分組,以便在特定情況下選擇性地執(zhí)行校驗(yàn)。開發(fā)人員可以為每個(gè)校驗(yàn)注解指定一個(gè)或多個(gè)校驗(yàn)組,然后在校驗(yàn)時(shí)選擇要執(zhí)行的校驗(yàn)組。
- 嵌套校驗(yàn):JSR-303允許對(duì)復(fù)雜對(duì)象進(jìn)行嵌套校驗(yàn),即在校驗(yàn)一個(gè)對(duì)象時(shí),也會(huì)對(duì)其關(guān)聯(lián)的其他對(duì)象進(jìn)行校驗(yàn)。這樣可以確保整個(gè)對(duì)象圖的完整性和合法性。
- 自定義校驗(yàn):JSR-303還允許開發(fā)人員定義自己的校驗(yàn)注解和校驗(yàn)器,以滿足特定的校驗(yàn)需求。通過(guò)實(shí)現(xiàn) ConstraintValidator 接口來(lái)自定義校驗(yàn)器,并在自定義注解中使用該校驗(yàn)器。
- 校驗(yàn)結(jié)果:JSR-303校驗(yàn)結(jié)果以校驗(yàn)異常的形式返回。當(dāng)校驗(yàn)失敗時(shí),會(huì)拋出 ConstraintViolationException 異常,其中包含了校驗(yàn)失敗的詳細(xì)信息,例如校驗(yàn)失敗的屬性、校驗(yàn)失敗的值、校驗(yàn)錯(cuò)誤消息等。
接下來(lái)我們將圍繞我們?cè)赟pring Boot的實(shí)體類中,使用JSR-303校驗(yàn)。值得注意的是,JSR-303校驗(yàn)我們一般都是對(duì)Java的實(shí)體類對(duì)象進(jìn)行校驗(yàn),主要檢驗(yàn)在我們的實(shí)體類對(duì)象的屬性上。
還是利用我們==>上一篇 <===的相關(guān)依賴文件,這篇我就不在重復(fù)導(dǎo)入相關(guān)依賴文件了。本篇著重文件主要正在User類和UserController,所用的依賴pom.xml文件和application.properties均與上篇一樣,就不在重復(fù)描述了。
PS: 你可以看到我在依賴中均采用了lombok,但是我卻并未使用這個(gè)玩意,原因在于,我使用了lombok
@ApiModel(description = "用戶實(shí)體")
public class User {
@ApiModelProperty("用戶編號(hào)")
private Long id;
@NotNull //校驗(yàn)定義的字段不能為空
@Size(min = 2, max = 5)
@ApiModelProperty("用戶姓名")
private String name;
@NotNull
@Max(100)
@Min(10)
@ApiModelProperty("用戶年齡")
private Integer age;
@NotNull
@Email
@ApiModelProperty("用戶郵箱")
private String email;
public User(Long id, String name, Integer age, String email) {
this.id = id;
this.name = name;
this.age = age;
this.email = email;
}
public User() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(name, user.name) && Objects.equals(age, user.age) && Objects.equals(email, user.email);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age, email);
}
}@Api(tags = "用戶管理")
@RestController
@RequestMapping(value = "/users") // 通過(guò)這里配置使下面的映射都在/users下
public class UserController {
// 創(chuàng)建線程安全的Map,模擬users信息的存儲(chǔ)
static Map<Long, User> users = Collections.synchronizedMap(new HashMap<>());
@GetMapping("/")
@ApiOperation(value = "獲取用戶列表")
public List<User> getUserList() {
List<User> r = new ArrayList<>(users.values());
return r;
}
@PostMapping("/")
@ApiOperation(value = "創(chuàng)建用戶", notes = "根據(jù)User對(duì)象創(chuàng)建用戶")
public String postUser(@Valid @RequestBody User user) {
users.put(user.getId(), user);
return "success";
}
@GetMapping("/{id}")
@ApiOperation(value = "獲取用戶詳細(xì)信息", notes = "根據(jù)url的id來(lái)獲取用戶詳細(xì)信息")
public User getUser(@PathVariable Long id) {
return users.get(id);
}
@PutMapping("/{id}")
@ApiImplicitParam(paramType = "path", dataType = "Long", name = "id", value = "用戶編號(hào)", required = true, example = "1")
@ApiOperation(value = "更新用戶詳細(xì)信息", notes = "根據(jù)url的id來(lái)指定更新對(duì)象,并根據(jù)傳過(guò)來(lái)的user信息來(lái)更新用戶詳細(xì)信息")
public String putUser(@PathVariable Long id, @RequestBody User user) {
User u = users.get(id);
u.setName(user.getName());
u.setAge(user.getAge());
users.put(id, u);
return "success";
}
@DeleteMapping("/{id}")
@ApiOperation(value = "刪除用戶", notes = "根據(jù)url的id來(lái)指定刪除對(duì)象")
public String deleteUser(@PathVariable Long id) {
users.remove(id);
return "success";
}
}接下來(lái)啟動(dòng)項(xiàng)目,當(dāng)然啟動(dòng)類還是需要加:@EnableSwagger2Doc這個(gè)注解
之后,我們可以通過(guò)使用相關(guān)工具比如Postman等測(cè)試工具發(fā)起,也可以使用curl發(fā)起,比如:

curl -X POST \
http://localhost:8080/users/ \
-H 'Content-Type: application/json' \
-H 'Postman-Token: 114db0f0-bdce-4ba5-baf6-01e5104a68a3' \
-H 'cache-control: no-cache' \
-d '{
"name": "abcdefg",
"age": 8,
"email": "aaaa"
}'得到相關(guān)信息:
{
"timestamp": "2019-10-05T06:24:30.518+0000",
"status": 400,
"error": "Bad Request",
"errors": [
{
"codes": [
"Size.user.name",
"Size.name",
"Size.java.lang.String",
"Size"
],
"arguments": [
{
"codes": [
"user.name",
"name"
],
"arguments": null,
"defaultMessage": "name",
"code": "name"
},
5,
2
],
"defaultMessage": "個(gè)數(shù)必須在2和5之間",
"objectName": "user",
"field": "name",
"rejectedValue": "abcdefg",
"bindingFailure": false,
"code": "Size"
},
{
"codes": [
"Min.user.age",
"Min.age",
"Min.java.lang.Integer",
"Min"
],
"arguments": [
{
"codes": [
"user.age",
"age"
],
"arguments": null,
"defaultMessage": "age",
"code": "age"
},
10
],
"defaultMessage": "最小不能小于10",
"objectName": "user",
"field": "age",
"rejectedValue": 8,
"bindingFailure": false,
"code": "Min"
},
{
"codes": [
"Email.user.email",
"Email.email",
"Email.java.lang.String",
"Email"
],
"arguments": [
{
"codes": [
"user.email",
"email"
],
"arguments": null,
"defaultMessage": "email",
"code": "email"
},
[],
{
"defaultMessage": ".*",
"codes": [
".*"
],
"arguments": null
}
],
"defaultMessage": "不是一個(gè)合法的電子郵件地址",
"objectName": "user",
"field": "email",
"rejectedValue": "aaaa",
"bindingFailure": false,
"code": "Email"
}
],
"message": "Validation failed for object='user'. Error count: 3",
"path": "/users/"
}其中返回名稱的各參數(shù)含義如下:
timestamp:請(qǐng)求時(shí)間status:HTTP返回的狀態(tài)碼,這里返回400,即:請(qǐng)求無(wú)效、錯(cuò)誤的請(qǐng)求,通常參數(shù)校驗(yàn)不通過(guò)均為400error:HTTP返回的錯(cuò)誤描述,這里對(duì)應(yīng)的就是400狀態(tài)的錯(cuò)誤描述:Bad Requesterrors:具體錯(cuò)誤原因,是一個(gè)數(shù)組類型;因?yàn)殄e(cuò)誤校驗(yàn)可能存在多個(gè)字段的錯(cuò)誤,比如這里因?yàn)槎x了兩個(gè)參數(shù)不能為Null,所以存在兩條錯(cuò)誤記錄信息message:概要錯(cuò)誤消息,返回內(nèi)容中很容易可以知道,這里的錯(cuò)誤原因是對(duì)user對(duì)象的校驗(yàn)失敗,其中錯(cuò)誤數(shù)量為2,而具體的錯(cuò)誤信息就定義在上面的errors數(shù)組中path:請(qǐng)求路徑
從errors數(shù)組中各個(gè)錯(cuò)誤明細(xì),知道各個(gè)字段的defaultMessage,可以看到很清晰的錯(cuò)誤描述。
瀏覽器訪問(wèn):
http://localhost:8080/swagger-ui.html


我們可以看到校驗(yàn)的部分限制。
到此這篇關(guān)于Spring Boot中使用JSR-303實(shí)現(xiàn)請(qǐng)求參數(shù)校驗(yàn)的文章就介紹到這了,更多相關(guān)springboot JSR-303請(qǐng)求參數(shù)校驗(yàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot中如何使用過(guò)濾器校驗(yàn)PSOT類型請(qǐng)求參數(shù)內(nèi)容
- SpringBoot常見get/post請(qǐng)求參數(shù)處理、參數(shù)注解校驗(yàn)及參數(shù)自定義注解校驗(yàn)詳解
- SpringBoot 如何自定義請(qǐng)求參數(shù)校驗(yàn)
- springboot整合JSR303參數(shù)校驗(yàn)與全局異常處理的方法
- springboot接口參數(shù)校驗(yàn)JSR303的實(shí)現(xiàn)
- SpringBoot使用jsr303校驗(yàn)的實(shí)現(xiàn)
- Springboot集成JSR303參數(shù)校驗(yàn)的方法實(shí)現(xiàn)
相關(guān)文章
Spring的異常處理@ExceptionHandler注解解析
這篇文章主要介紹了Spring的異常處理@ExceptionHandler注解解析,當(dāng)一個(gè)Controller中有方法加了@ExceptionHandler之后,這個(gè)Controller其他方法中沒(méi)有捕獲的異常就會(huì)以參數(shù)的形式傳入加了@ExceptionHandler注解的那個(gè)方法中,需要的朋友可以參考下2023-12-12
Java+Swing實(shí)現(xiàn)醫(yī)院管理系統(tǒng)的完整代碼
這篇文章主要介紹了Java+Swing實(shí)現(xiàn)醫(yī)院管理系統(tǒng)的完整代碼,代碼簡(jiǎn)單易懂,對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05
Springboot整合分頁(yè)插件PageHelper步驟解析
這篇文章主要介紹了Springboot整合分頁(yè)插件PageHelper步驟解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06
java聯(lián)系人管理系統(tǒng)簡(jiǎn)單設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了java聯(lián)系人管理系統(tǒng)簡(jiǎn)單設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10
Java Selenium實(shí)現(xiàn)多窗口切換的示例代碼
這篇文章主要介紹了Java Selenium實(shí)現(xiàn)多窗口切換的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09

