Spring Boot中使用JSR-303實(shí)現(xiàn)請求參數(shù)校驗(yàn)
JSR-303是Java中的一個(gè)規(guī)范,用于實(shí)現(xiàn)請求參數(shù)校驗(yàn)。它定義了一組注解,可以應(yīng)用于JavaBean的字段上,用于驗(yàn)證輸入?yún)?shù)的合法性。下面是一些常用的JSR-303注解及其介紹:
@NotNull
:用于驗(yàn)證字段值不能為null。@NotEmpty
:用于驗(yàn)證字符串字段不能為空。@NotBlank
:用于驗(yàn)證字符串字段不能為空,并且長度必須大于0。@Min
:用于驗(yàn)證數(shù)字字段的最小值。@Max
:用于驗(yàn)證數(shù)字字段的最大值。@Size
:用于驗(yàn)證字符串、集合或數(shù)組字段的長度或大小。@Pattern
:用于驗(yàn)證字符串字段是否匹配指定的正則表達(dá)式。@Email
:用于驗(yàn)證字符串字段是否符合Email格式。@Range
:用于驗(yàn)證數(shù)字字段的取值范圍。@Valid
:用于嵌套驗(yàn)證,可以對對象中的字段進(jìn)行遞歸驗(yàn)證。
其他如下圖所示:
通過在JavaBean的字段上添加這些注解,可以在接收請求參數(shù)時(shí)進(jìn)行自動(dòng)校驗(yàn)。如果校驗(yàn)失敗,可以通過異常處理機(jī)制來處理校驗(yàn)錯(cuò)誤。
需要注意的是,JSR-303只提供了基本的驗(yàn)證注解,如果需要更復(fù)雜的校驗(yàn)邏輯,可以自定義注解或使用第三方庫,如Hibernate Validator等。
Hibernate Validator附加的constraint:
至于我們?yōu)樯妒褂肑SR-303來校驗(yàn)我們的參數(shù),主要是為了解決我們在實(shí)際開發(fā)過程中,前后端的參數(shù)校驗(yàn)導(dǎo)致的問題。比如:
- 我們依靠前端框架解決參數(shù)校驗(yàn),但是缺少服務(wù)器的參數(shù)校驗(yàn),這種情況常見于需要同時(shí)進(jìn)行開發(fā)前后端的時(shí)候,雖然程序的正常使用不會有問題,到那時(shí)開發(fā)者會忽略非正常的操作,比如繞過前端程序,直接模擬客戶端請求,這樣就繞過了我們對前端預(yù)設(shè)的各種限制,直擊我們后端的數(shù)據(jù)訪問接口,進(jìn)而使得我們的后端系統(tǒng)存在嚴(yán)重的安全隱患。
- 大量的利用if/else語句嵌套實(shí)現(xiàn),使得我們的校驗(yàn)邏輯晦澀難懂,并不利于我們對整個(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ī):
- 在對象被持久化之前(例如,保存到數(shù)據(jù)庫之前)。
- 在對象被修改之后(例如,更新數(shù)據(jù)庫中的記錄后)。
- 校驗(yàn)約束注解:JSR-303標(biāo)準(zhǔn)提供了一組注解,可以用于對Java對象的屬性進(jìn)行校驗(yàn)。這些注解包括但不限于
@NotNull
、@NotEmpty
、@Min
、@Max
、@Size
、@Pattern
、@Email
等。開發(fā)人員可以根據(jù)需求選擇適當(dāng)?shù)淖⒔鈦矶x校驗(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允許對復(fù)雜對象進(jìn)行嵌套校驗(yàn),即在校驗(yàn)一個(gè)對象時(shí),也會對其關(guān)聯(lián)的其他對象進(jìn)行校驗(yàn)。這樣可以確保整個(gè)對象圖的完整性和合法性。
- 自定義校驗(yàn):JSR-303還允許開發(fā)人員定義自己的校驗(yàn)注解和校驗(yàn)器,以滿足特定的校驗(yàn)需求。通過實(shí)現(xiàn) ConstraintValidator 接口來自定義校驗(yàn)器,并在自定義注解中使用該校驗(yàn)器。
- 校驗(yàn)結(jié)果:JSR-303校驗(yàn)結(jié)果以校驗(yàn)異常的形式返回。當(dāng)校驗(yàn)失敗時(shí),會拋出 ConstraintViolationException 異常,其中包含了校驗(yàn)失敗的詳細(xì)信息,例如校驗(yàn)失敗的屬性、校驗(yàn)失敗的值、校驗(yàn)錯(cuò)誤消息等。
接下來我們將圍繞我們在Spring Boot的實(shí)體類中,使用JSR-303校驗(yàn)。值得注意的是,JSR-303校驗(yàn)我們一般都是對Java的實(shí)體類對象進(jìn)行校驗(yàn),主要檢驗(yàn)在我們的實(shí)體類對象的屬性上。
還是利用我們==>上一篇 <===的相關(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("用戶編號") 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") // 通過這里配置使下面的映射都在/users下 public class UserController { // 創(chuàng)建線程安全的Map,模擬users信息的存儲 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對象創(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來獲取用戶詳細(xì)信息") public User getUser(@PathVariable Long id) { return users.get(id); } @PutMapping("/{id}") @ApiImplicitParam(paramType = "path", dataType = "Long", name = "id", value = "用戶編號", required = true, example = "1") @ApiOperation(value = "更新用戶詳細(xì)信息", notes = "根據(jù)url的id來指定更新對象,并根據(jù)傳過來的user信息來更新用戶詳細(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來指定刪除對象") public String deleteUser(@PathVariable Long id) { users.remove(id); return "success"; } }
接下來啟動(dòng)項(xiàng)目,當(dāng)然啟動(dòng)類還是需要加:@EnableSwagger2Doc
這個(gè)注解
之后,我們可以通過使用相關(guān)工具比如Postman等測試工具發(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
:請求時(shí)間status
:HTTP返回的狀態(tài)碼,這里返回400,即:請求無效、錯(cuò)誤的請求,通常參數(shù)校驗(yàn)不通過均為400error
:HTTP返回的錯(cuò)誤描述,這里對應(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ò)誤原因是對user對象的校驗(yàn)失敗,其中錯(cuò)誤數(shù)量為2,而具體的錯(cuò)誤信息就定義在上面的errors數(shù)組中path
:請求路徑
從errors數(shù)組中各個(gè)錯(cuò)誤明細(xì),知道各個(gè)字段的defaultMessage,可以看到很清晰的錯(cuò)誤描述。
瀏覽器訪問:
http://localhost:8080/swagger-ui.html
我們可以看到校驗(yàn)的部分限制。
到此這篇關(guān)于Spring Boot中使用JSR-303實(shí)現(xiàn)請求參數(shù)校驗(yàn)的文章就介紹到這了,更多相關(guān)springboot JSR-303請求參數(shù)校驗(yàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Springboot中如何使用過濾器校驗(yàn)PSOT類型請求參數(shù)內(nèi)容
- SpringBoot常見get/post請求參數(shù)處理、參數(shù)注解校驗(yàn)及參數(shù)自定義注解校驗(yàn)詳解
- SpringBoot 如何自定義請求參數(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其他方法中沒有捕獲的異常就會以參數(shù)的形式傳入加了@ExceptionHandler注解的那個(gè)方法中,需要的朋友可以參考下2023-12-12Java+Swing實(shí)現(xiàn)醫(yī)院管理系統(tǒng)的完整代碼
這篇文章主要介紹了Java+Swing實(shí)現(xiàn)醫(yī)院管理系統(tǒng)的完整代碼,代碼簡單易懂,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-05-05Springboot整合分頁插件PageHelper步驟解析
這篇文章主要介紹了Springboot整合分頁插件PageHelper步驟解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-06-06java聯(lián)系人管理系統(tǒng)簡單設(shè)計(jì)
這篇文章主要為大家詳細(xì)介紹了java聯(lián)系人管理系統(tǒng)簡單設(shè)計(jì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-10-10Java Selenium實(shí)現(xiàn)多窗口切換的示例代碼
這篇文章主要介紹了Java Selenium實(shí)現(xiàn)多窗口切換的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-09-09