[TOC]
簡(jiǎn)介
后端編程中觉增,通常對(duì)于前端傳遞過(guò)來(lái)的數(shù)據(jù),我們都需要進(jìn)行校驗(yàn)翻斟,確保數(shù)據(jù)正確且安全逾礁。
最直接的方法當(dāng)然是在 Controller 相應(yīng)方法內(nèi)對(duì)數(shù)據(jù)進(jìn)行手動(dòng)校驗(yàn),但是访惜,由于很多校驗(yàn)都具備相似性嘹履,因此這種做法稍顯冗余。
因此债热,相關(guān)的校驗(yàn)規(guī)范就應(yīng)運(yùn)而生砾嫉。比如:
-
JSR-303:它是一項(xiàng) Bean Validation 校驗(yàn)標(biāo)準(zhǔn),規(guī)定了一些校驗(yàn)規(guī)范窒篱,比如
@Null
焕刮,@NotNull
,@Pattern
墙杯,相關(guān)注解都位于javax.validation.constraints
包下配并。需要注意的是,JSR-303 只提供校驗(yàn)規(guī)范高镐,不提供實(shí)現(xiàn)溉旋。
JSR-303 是 Bean Validation 1.0 版本,隨著越來(lái)越多的新規(guī)范并入嫉髓,它的版本也一直在更新观腊,比如,JSR-349 就是 Bean Validation 1.1 版本算行,而當(dāng)前最新的版本為 JSR-380梧油,也即 Bean Validation 2.0 版本...
由于 JSR-303 只提供規(guī)范,因此其實(shí)現(xiàn)需要其他庫(kù)進(jìn)行提供州邢。當(dāng)前使用最廣泛的 Bean Validation 實(shí)現(xiàn)庫(kù)為:hibernate-validator儡陨。
hibernate-validator 是對(duì) JSR-303 的實(shí)現(xiàn),同時(shí)它也增添了其他一些校驗(yàn)注解偷霉,比如迄委,@URL
,@Length
类少,@Ranger
等叙身。
而在 Spring 中,其也提供了相應(yīng)的 Bean Validation 實(shí)現(xiàn):Java Bean Validation硫狞。
Spring Validation 主要是對(duì) hibernate-validator 進(jìn)行了二次封裝信轿,并在 SpringMVC 中添加了自動(dòng)校驗(yàn)晃痴,以及將校驗(yàn)信息封裝進(jìn)特定類(lèi)中等功能。
本文主要介紹下在 Spring Boot 中進(jìn)行數(shù)據(jù)校驗(yàn)(Bean Validation)财忽。
依賴(lài)添加
Spring Boot 中進(jìn)行數(shù)據(jù)校驗(yàn)需要添加起步依賴(lài):spring-boot-starter-validation
倘核,如下所示:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
注:在spring-boot-starter-web
舊版本中,其內(nèi)置了spring-boot-starter-validation
即彪,但是 Spring Boot 官方似乎認(rèn)為并不是很多應(yīng)用會(huì)使用數(shù)據(jù)校驗(yàn)功能岳链,因此對(duì)其進(jìn)行了移除迅办。具體請(qǐng)參考:issue#19550。
基本使用
數(shù)據(jù)校驗(yàn)最基本的操作就是使用相關(guān)注解對(duì)一個(gè) Java Bean 內(nèi)的相關(guān)字段進(jìn)行約束,然后前端傳遞上來(lái)的數(shù)據(jù)會(huì)首先組裝為相應(yīng)的 Java Bean 對(duì)象奴饮,該對(duì)象會(huì)被移交到一個(gè)Validator
黍翎,讓其檢查對(duì)象字段(即數(shù)據(jù))是否滿(mǎn)足約束盏浙,如果不滿(mǎn)足的話(huà)佩研,則會(huì)通過(guò)如拋出異常等方式通知系統(tǒng)。
具體的使用步驟如下所示:
-
首先定義一個(gè)需要校驗(yàn)的 Java Bean 類(lèi):
@Data public class User { private int id; @NotBlank(message = "用戶(hù)名不能為空") private String name; @NotNull(message = "請(qǐng)輸入密碼") @Length(min = 6, max = 10, message = "密碼為 6 到 10 位") private String password; @Email private String email; }
上述代碼中舞终,我們使用
@NotBlank
轻庆、@NotNull
、@Length
和@Email
等注解對(duì)User
類(lèi)中的相應(yīng)字段進(jìn)行了約束敛劝。
各注解對(duì)應(yīng)的約束內(nèi)容請(qǐng)參考后文余爆。 -
在 Controller 相應(yīng)接口方法中,使用
@Valid
/@Validated
等注解開(kāi)啟數(shù)據(jù)校驗(yàn)功能:@RestController @RequestMapping("validate") public class ValidationController { @PostMapping("/user") public String addUser(@Validated @RequestBody User user){ return "add user successfully! " + user; } }
-
如果數(shù)據(jù)校驗(yàn)不通過(guò)攘蔽,就會(huì)拋出一個(gè)
MethodArgumentNotValidException
異常龙屉。默認(rèn)情況下呐粘,Spring 會(huì)將該異常及其信息以錯(cuò)誤碼 400 進(jìn)行下發(fā)满俗。我們可以通過(guò)自定義一個(gè)全局異常捕獲器攔截該異常,提取出數(shù)據(jù)校驗(yàn)出錯(cuò)信息作岖,進(jìn)行展示:@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(MethodArgumentNotValidException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public String handlerMethodArgumentNotValidException(MethodArgumentNotValidException e) { return e.getBindingResult().getFieldErrors() .stream() .map(fieldError -> { return String.format("[%s: %s]\n", fieldError.getField(), fieldError.getDefaultMessage()); }).collect(Collectors.joining()); } }
以上唆垃,就完成了一個(gè)基礎(chǔ)的數(shù)據(jù)校驗(yàn)功能。
此時(shí)我們進(jìn)行如下訪(fǎng)問(wèn):
$ curl http://localhost:8080/validate/user --header "Content-Type: application/json;charset=UTF-8" -X POST --data "{\"password\": \"123456\"}"
[name: 用戶(hù)名不能為空]
$ curl http://localhost:8080/validate/user --header "Content-Type: application/json;charset=UTF-8" -X POST --data "{\"name\": \"Whyn\",\"password\": \"12345\"}"
[password: 密碼為 6 到 10 位]
$ curl http://localhost:8080/validate/user --header "Content-Type: application/json;charset=UTF-8" -X POST --data "{\"name\": \"Whyn\",\"password\": \"123456\"}"
add user successfully! User(id=0, name=Whyn, password=123456, email=null)
可以看到痘儡,結(jié)果符合預(yù)期辕万。
注:上述代碼如果數(shù)據(jù)校驗(yàn)不通過(guò),就會(huì)拋出MethodArgumentNotValidException
沉删,其實(shí)是因?yàn)槲覀冊(cè)跒閰?shù)注解了@RequestBody
渐尿,此時(shí)HttpMessageConverter
會(huì)負(fù)責(zé)轉(zhuǎn)換過(guò)程,當(dāng)遇到數(shù)據(jù)校驗(yàn)失敗時(shí)矾瑰,就會(huì)拋出MethodArgumentNotValidException
砖茸。
而如果去除@RequestBody
注解,默認(rèn)就會(huì)由@ModelAttribute
負(fù)責(zé)數(shù)據(jù)綁定和校驗(yàn)殴穴,如果此時(shí)校驗(yàn)失敗凉夯,則會(huì)拋出BindException
(更多詳情货葬,可參考:issue#14790),因此劲够,為了程序更加健壯震桶,最好為我們的全局異常處理器增加BindException
異常捕獲。如下所示:
@RestControllerAdvice
public class GlobalExceptionHandler {
...
@ExceptionHandler(BindException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleBindException(BindException e){
return e.getBindingResult().getFieldErrors()
.stream()
.map(fieldError -> {
return String.format("[%s: %s]\n", fieldError.getField(), fieldError.getDefaultMessage());
}).collect(Collectors.joining());
}
}
此時(shí)征绎,請(qǐng)求上述代碼蹲姐,結(jié)果如下:
$ curl http://localhost:8080/validate/user -X POST
[name: 用戶(hù)名不能為空]
[password: 請(qǐng)輸入密碼]
$ curl http://localhost:8080/validate/user -X POST --data "name=Whyn"
[password: 請(qǐng)輸入密碼]
$ curl http://localhost:8080/validate/user -X POST --data "name=Whyn" --data "password=123456"
add user successfully! User(id=0, name=Whyn, password=123456, email=null, phoneNo=null)
上面是對(duì)復(fù)雜數(shù)據(jù)(Java Bean)的校驗(yàn)使用方式,而如果前端傳遞的是簡(jiǎn)單基本類(lèi)型(比如String
)或者是對(duì)路徑變量(Path Variable)進(jìn)行校驗(yàn)人柿,可使用如下方式:
@RestController
@RequestMapping("validate")
@Validated
public class ValidationController {
@GetMapping("/user/{id}")
public String getUser(@PathVariable("id") @Min(10) int id) {
return "User id is " + id;
}
@PutMapping("/user")
public String updateUser(@RequestParam("name") @NotBlank String name,
@RequestParam("email") @Email String email) {
User user = new User();
user.setName(name);
user.setEmail(email);
return "update user done: " + user;
}
}
可以看到淤堵,對(duì)于簡(jiǎn)單數(shù)據(jù)類(lèi)型,我們將約束注解直接注解到相應(yīng)參數(shù)上顷扩,然后在Controller
類(lèi)上使用@Validated
注解拐邪,啟動(dòng)數(shù)據(jù)校驗(yàn)。
對(duì)于這種數(shù)據(jù)校驗(yàn)方式隘截,當(dāng)校驗(yàn)失敗時(shí)扎阶,會(huì)拋出ConstraintViolationException
,而不是我們上面對(duì) Java Bean 校驗(yàn)失敗拋出的MethodArgumentNotValidException
異常婶芭,因此东臀,可以為我們的全局異常處理器捕獲該異常,進(jìn)行處理犀农。如下所示:
@RestControllerAdvice
public class GlobalExceptionHandler {
...
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleConstraintViolationException(ConstraintViolationException e) {
return e.getConstraintViolations()
.stream()
.map(constraintViolation -> {
return String.format("[%s: %s]\n",
constraintViolation.getPropertyPath().toString(),
constraintViolation.getMessage());
}).collect(Collectors.joining());
}
}
請(qǐng)求上述代碼惰赋,如下所示:
$ curl -X GET http://localhost:8080/validate/user/1
[getUser.id: must be greater than or equal to 10]
$ curl -X GET http://localhost:8080/validate/user/10
User id is 10
$ curl http://localhost:8080/validate/user -X PUT --data "name=" --data "email=10"
[updateUser.name: must not be blank]
[updateUser.email: must be a well-formed email address]
$ curl http://localhost:8080/validate/user -X PUT --data "name=Whyn" --data "email=10@qq.com"
update user done: User(id=0, name=Whyn, password=null, email=10@qq.com, extraInfo=null)
Bean Validation 相關(guān)注解
-
下面主要介紹下 JSR 中一些常用的相關(guān)約束注解,如下所示:
注解 釋義 可被注解元素類(lèi)型 @NotNull
被注解的元素不能為 null
所有類(lèi)型 @NotBlank
被注解的元素不能為 null
呵哨,且至少包含一個(gè)非空白字符支持 CharSequence
@NotEmpty
被注解的元素不能為 null
赁濒,且不能為空(即不能為空集合)支持 CharSequence
、Collection
孟害、Map
拒炎、Array
@Min(value)
被注解的元素值必須大于或等于 @Min
指定的值支持 BigDecimal
、BigInteger
挨务,以及byte
击你、short
等基本數(shù)值類(lèi)型及其他們相應(yīng)的包裝類(lèi)型@Max(value)
被注解的元素值必須小于或等于 @Max
指定的值支持 BigDecimal
、BigInteger
谎柄,以及byte
丁侄、short
等基本數(shù)值類(lèi)型及其他們相應(yīng)的包裝類(lèi)型@Size(max=, min=)
被注解的元素大小必須在指定的范圍內(nèi) CharSequence
、Collection
朝巫、Map
鸿摇、Array
以及null
。
注:null
元素會(huì)被認(rèn)為是有效值@Pattern
被注解的元素必須符合指定的正則匹配 CharSequence
注:null
類(lèi)型元素會(huì)被認(rèn)為是有效值@AssertTrue
被注解的元素值必須為 true
支持 boolean
捍歪、Boolean
類(lèi)型@AssertFalse
被注解的元素值必須為 false
支持 boolean
户辱、Boolean
類(lèi)型更多 JSR 相關(guān)注解內(nèi)容鸵钝,請(qǐng)參考:javax.validation.constraints
-
下面介紹下 hibernate-validator 的一些常用特有注解:
注解 釋義 可被注解元素類(lèi)型 @Length(min=,max=)
被注解的字符串長(zhǎng)度必須在指定范圍內(nèi) 字符串 @Range(min=,max=)
被注解的元素必須在指定范圍內(nèi) 數(shù)值類(lèi)型或者數(shù)值字符串類(lèi)型 @URL
被注解的字符串匹配 URL 字符串 更多 hibernate-validator 相關(guān)注解內(nèi)容,請(qǐng)參考:org.hibernate.validator.constraints
-
下面介紹下 Spring Bean Validation 的一些常用特有注解:
注解 釋義 可被注解元素類(lèi)型 @Validated
開(kāi)啟數(shù)據(jù)校驗(yàn)功能庐镐,支持分組校驗(yàn) 任何非原子類(lèi)型 更多 Spring Bean Validation 相關(guān)注解內(nèi)容恩商,請(qǐng)參考:org.springframework.validation.annotation
注:
@Validated
注解是@Valid
注解的一個(gè)變種實(shí)現(xiàn),它們都主要用于啟動(dòng)數(shù)據(jù)校驗(yàn)功能必逆,而不同之處大致有以下幾方面:@Valid
是屬于 JSR 規(guī)范怠堪,其位于包javax
內(nèi);而@Validated
是屬于 Spring Bean Validation名眉,其位于包org.springframework.validation
內(nèi)粟矿。-
@Valid
支持嵌套校驗(yàn)(就是一個(gè) Bean 內(nèi)嵌套另一個(gè) Bean),而@Validated
不支持损拢。如下所示:@Data public class User { ... @Valid // 嵌套校驗(yàn) private ExtraInfo extraInfo; @Data public static class ExtraInfo { @Pattern(regexp = "\\b(male|female)\\b", message = "male or female") @NotBlank(message = "性別不能為空") private String sex; @Min(0) @Max(130) private int age; } }
注:嵌套校驗(yàn)只需要求嵌套 Bean 內(nèi)使用
@Valid
注解陌粹,而啟動(dòng)數(shù)據(jù)校驗(yàn)(即 Controller 層)使用@Valid
或者@Validated
都可以。請(qǐng)求上述代碼福压,如下所示:
$ curl http://localhost:8080/validate/user --header "Content-Type: application/json;charset=UTF-8" -X POST --data "{\"name\": \"Whyn\",\"password\": \"123456\",\"extraInfo\": {\"sex\": \"男\(zhòng)"}}" [extraInfo.sex: male or female] $ curl http://localhost:8080/validate/user --header "Content-Type: application/json;charset=UTF-8" -X POST --data "{\"name\": \"Whyn\",\"password\": \"123456\",\"extraInfo\": {\"sex\": \"male\"}}" add user successfully! User(id=0, name=Whyn, password=123456, email=null, extraInfo=User.ExtraInfo(sex=male, age=0))
-
@Validated
支持分組校驗(yàn)功能掏秩,而@Valid
不支持。啟動(dòng)分組校驗(yàn)步驟如下所示:- 首先創(chuàng)建兩個(gè)分組接口:
public interface ValidationGroup1 {} public interface ValidationGroup2 {}
- 在實(shí)體類(lèi)中添加分組信息:
@Data public class User { private int id; // 隸屬分組 1 @NotBlank(message = "用戶(hù)名不能為空", groups = ValidationGroup1.class) private String name; // 隸屬分組 1 和 2 @NotNull(message = "請(qǐng)輸入密碼", groups = {ValidationGroup1.class, ValidationGroup2.class}) // 不進(jìn)行分組 @Length(min = 6, max = 10, message = "密碼為 6 到 10 位") private String password; // 不進(jìn)行分組 @Email private String email; }
- 使用
@Validated
指定分組:
@RestController @RequestMapping("validate") public class ValidationController { @PostMapping("/user") public String addUser(@Validated(ValidationGroup2.class) @RequestBody User user){ return "add user successfully! " + user; } }
上述代碼我們指定使用分組
ValidationGroup2
進(jìn)行數(shù)據(jù)校驗(yàn)荆姆,ValidationGroup2
只對(duì)password
進(jìn)行NotNull
約束蒙幻,因此,只要我們發(fā)送的數(shù)據(jù)滿(mǎn)足password
不為null
胆筒,就可以通過(guò)校驗(yàn)邮破,如下所示:$ curl http://localhost:8080/validate/user --header "Content-Type: application/json;charset=UTF-8" -X POST --data "{\"name\": \"Whyn\"}" [password: 請(qǐng)輸入密碼] $ curl http://localhost:8080/validate/user --header "Content-Type: application/json;charset=UTF-8" -X POST --data "{\"name\": \"Whyn\",\"password\": \"\"}" add user successfully! User(id=0, name=Whyn, password=, email=null)
注:分組校驗(yàn)的一個(gè)問(wèn)題就是,對(duì)于未指定分組的其他校驗(yàn)仆救,直接忽略抒和,通常這并不是我們想要的結(jié)果。對(duì)于未指定分組的校驗(yàn)派桩,我們通常期望的是构诚,無(wú)論使用哪種分組校驗(yàn),這些未指定的分組校驗(yàn)均生效铆惑。
實(shí)際上,未指定分組的校驗(yàn)都?xì)w類(lèi)為 默認(rèn)分組(Default
)送膳,且分組支持繼承员魏,子類(lèi)分組可完全繼承父類(lèi)分組的約束校驗(yàn),因此叠聋,只需讓我們的自定義分組繼承默認(rèn)分組撕阎,即可完成分組校驗(yàn)以及默認(rèn)分組生效,代碼如下:public interface ValidationGroup1 extends Default {} public interface ValidationGroup2 extends Default {}
綜上碌补,一個(gè)比較推薦的使用方式就是:?jiǎn)?dòng)校驗(yàn)(即 Controller 層)時(shí)使用
@Validated
注解虏束,嵌套校驗(yàn)時(shí)使用@Valid
注解棉饶,這樣,就能同時(shí)使用分組校驗(yàn)和嵌套校驗(yàn)功能镇匀。
自定義Validator
前文講過(guò)照藻,數(shù)據(jù)校驗(yàn)功能是由Validator
負(fù)責(zé)開(kāi)啟并校驗(yàn)的,在 SpringMVC 中汗侵,如果檢測(cè)到 Bean Validation(比如幸缕,Hibernate Validator)存在于classpath
路徑上時(shí),就會(huì)默認(rèn)全局注冊(cè)了一個(gè)Validator
:LocalValidatorFactoryBean
晰韵,它會(huì)驅(qū)動(dòng)@Valid
和@Validated
開(kāi)啟數(shù)據(jù)校驗(yàn)发乔。
LocalValidatorFactoryBean
同時(shí)實(shí)現(xiàn)了javax.validation.ValidatorFactory
、javax.validation.Validator
和org.springframework.validation.Validator
三個(gè)接口雪猪,所以如果需要手動(dòng)調(diào)用數(shù)據(jù)校驗(yàn)邏輯栏尚,可以通過(guò) IOC 容器獲取到這些接口的實(shí)例。如下所示:
- 獲取
javax.validation.Validator
接口實(shí)例:import javax.validation.Validator; @Service public class MyService { @Autowired private Validator validator; }
- 獲取
org.springframework.validation.Validator
接口實(shí)例:import org.springframework.validation.Validator; @Service public class MyService { @Autowired private Validator validator; }
上述獲取的是系統(tǒng)默認(rèn)的Validator
只恨,而如果我們想注入一個(gè)自定義Validator
抵栈,有如下幾種方法:
-
注入自定義
Validator
到 Spring IOC 容器:import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; @Configuration public class AppConfig { @Bean public LocalValidatorFactoryBean validator() { return new LocalValidatorFactoryBean(); } }
-
為 SpringMVC 配置一個(gè)全局
Validator
:@Configuration @EnableWebMvc public class WebConfig implements WebMvcConfigurer { @Override public Validator getValidator() { // ... } }
也可以為單獨(dú)一個(gè) Controller 設(shè)置一個(gè)局部
Validator
,如下所示:@Controller public class MyController { @InitBinder protected void initBinder(WebDataBinder binder) { binder.addValidators(new FooValidator()); } }
自定義約束注解
如果現(xiàn)存的約束注解無(wú)法滿(mǎn)足我們的需求坤次,那么我們可以通過(guò)自定義約束注解古劲,來(lái)定制我們的數(shù)據(jù)校驗(yàn)邏輯。
在 Spring 中缰猴,自定義約束注解主要就是定義一個(gè)約束注解及其對(duì)應(yīng)的Validator
产艾,兩者通過(guò)@Constraint
關(guān)聯(lián)到一起。
默認(rèn)情況下滑绒,全局校驗(yàn)器LocalValidatorFactoryBean
會(huì)配置一個(gè)SpringConstraintValidatorFactory
實(shí)例闷堡,SpringConstraintValidatorFactory
實(shí)現(xiàn)了接口ConstraintValidatorFactory
,因此它會(huì)在遇到自定義約束注解的時(shí)候疑故,就會(huì)自動(dòng)實(shí)例化@Constraint
指定的關(guān)聯(lián)Validator
杠览,從而完成數(shù)據(jù)校驗(yàn)過(guò)程。
詳細(xì)過(guò)程可參考如下示例:
例子:假設(shè)我們想自定義一個(gè)約束注解纵势,用于對(duì)手機(jī)號(hào)進(jìn)行校驗(yàn)踱阿,要求滿(mǎn)足手機(jī)號(hào)碼的格式為:+86 13699328716
,即以+86
開(kāi)頭钦铁,然后中間一個(gè)或多個(gè)空格软舌,后面是有效的手機(jī)號(hào)碼。
自定義約束注解的步驟如下所示:
-
自定義一個(gè)約束注解:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = PhoneNoConstraintValidator.class) public @interface PhoneNoConstraint { String message() default "手機(jī)號(hào)碼格式錯(cuò)誤"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
這里通過(guò)注解
@Constraint
將自定義注解PhoneNoConstraint
與PhoneNoConstraintValidator
(即一個(gè)自定義Validator
)關(guān)聯(lián)到一起牛曹。 -
自定義一個(gè)
Validator
:public class PhoneNoConstraintValidator implements ConstraintValidator<PhoneNoConstraint, String> { @Override public boolean isValid(String value, ConstraintValidatorContext context) { String regex = "\\+86\\s+\\d{11}"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(value); return matcher.matches(); } }
-
使用自定義約束注解:
@RestController @RequestMapping("validate") @Validated public class ValidationController { @PostMapping("/user/{id}") public String addPhoneNo(@PathVariable("id") int id, @RequestParam("phoneNo") @NotBlank(message = "手機(jī)號(hào)不能為空") @PhoneNoConstraint(message = "手機(jī)號(hào)必須以 +86 開(kāi)頭") String phoneNo) { return id + " => add phoneNo done: " + phoneNo; } }
當(dāng)程序運(yùn)行時(shí)佛点,遇到自定義約束注解
@PhoneNoConstraint
時(shí),SpringConstraintValidatorFactory
就會(huì)通過(guò)@PhoneNoConstraint
上的@Constraint
注解,獲取得到其對(duì)應(yīng)的Valiator
超营,然后通過(guò) Spring 創(chuàng)建該Validator
實(shí)例鸳玩,進(jìn)行數(shù)據(jù)校驗(yàn)。利用這種機(jī)制演闭,可以使得我們的自定義Validator
享受到其他 Java Bean 一樣的依賴(lài)注入功能不跟。請(qǐng)求上述代碼,結(jié)果如下:
$ curl localhost:8080/validate/user/1 -X POST --data-urlencode "phoneNo=13699328716" [addPhoneNo.phoneNo: 手機(jī)號(hào)必須以 +86 開(kāi)頭] $ curl localhost:8080/validate/user/1 -X POST --data-urlencode "phoneNo=+86 13699328716" 1 => add phoneNo done: +86 13699328716
注:如果 URL 包含
+
船响、=
躬拢、&
等特殊符號(hào)時(shí),會(huì)被進(jìn)行轉(zhuǎn)義见间,比如聊闯,+
會(huì)被轉(zhuǎn)義為空格,這樣后端接收的數(shù)據(jù)格式就永遠(yuǎn)是錯(cuò)誤的米诉,因此菱蔬,發(fā)送數(shù)據(jù)前,應(yīng)先對(duì)數(shù)據(jù)進(jìn)行編碼史侣,所以上述curl
命令使用--data-urlencode
對(duì)數(shù)據(jù)進(jìn)行編碼拴泌,以確保特殊字符能成功發(fā)送。
其他
- 除了對(duì) Controller 層添加數(shù)據(jù)校驗(yàn)外惊橱,還可以為 Spring 其他組件添加數(shù)據(jù)校驗(yàn)功能蚪腐,只需結(jié)合
@Validated
和@Valid
這兩個(gè)注解。
比如税朴,對(duì) Serivce 層添加數(shù)據(jù)校驗(yàn)功能回季,如下所示:@Service @Validated class ValidatingService{ void validateInput(@Valid Input input){ // do something } }