相關知識
spring-boot-starter-web
項目中默認已經集成了 hibernate-validator
常用註解
JSR 303 Bean Validation
註解 | 說明 | 數據類型 |
---|---|---|
AssertTrue | 標註元素必須爲true | Boolean |
AssertFalse | 標註元素必須爲false | Boolean |
DecimalMax(value,isclusive) | 標註元素必須小於等於指定值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
DecimalMin(value,isclusive) | 標註元素必須大於等於指定值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
Digits(integer,fraction) | 標註元素必須位於指定位數之內 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
Email(regexp,flags) | 標註元素必須爲格式正確的郵件地址 | CharSequence |
Future | 標註元素必須爲將來的日期 | Date,Calendar,Instant, LocalDate,LocalDateTime, LocalTime,MonthDay, OffsetDateTime,OffsetTime, Year,YearMonth, ZonedDateTime,HijrahDate, JapaneseDate,MinguoDate, ThaiBuddhistDate |
FutureOrPresent | 標註元素必須爲現在或將來的日期 | 同Future |
Max(value) | 標註元素必須小於等於指定值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
Min(value) | 標註元素必須大於等於指定值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
Negative | 標註元素必須爲嚴格負值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
NegativeOrZero | 標註元素必須爲嚴格的負值或者0值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
NotBlank | 標註元素必須不爲null,且必須包含至少一個非空字符 | CharSequence |
NotEmpty | 標註元素必須不爲null,且必須包含至少一個子元素 | CharSequence,Collection,Map,Array |
NotNull | 標註元素必須不爲null | all |
Null | 標註元素必須爲null | all |
Past | 標註元素必須爲過去的日期 | 同Future |
PastOrPresent | 標註元素必須爲過去的或者現在的日期 | 同Future |
Pattern(regexp,flags) | 標註元素必須匹配給定的正則表達式 | CharSequence |
Positive | 標註元素必須爲嚴格的正值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
PositiveOrZero | 標註元素必須爲嚴格的正值或者0值 | BigDecimal,BigInteger, CharSequence,byte,short, int, long,Byte,Short, Integer,Long |
Size(min,max) | 標註元素必須在指定範圍之內 | CharSequence,Collection,Map,Array |
Hibernate Validation
註解 | 說明 | 備註 |
---|---|---|
Length(min,max) | 標註元素的長度必須在指定範圍之內,包含最大值 | 字符串 |
Range(min,max) | 標註元素值必須在指定範圍之內 | 數字值,或者其字符串形式 |
URL(regexp,flags) | 標註元素必須爲格式正確的URL | 字符串 |
URL(protocol,host,port) | 標註元素必須滿足給定的協議主機和端口號 | 字符串 |
目標
整合 hibernate-validator,使用註解的方式對接口入參進行校驗。
操作步驟
添加依賴
spring-boot-starter-web
已經默認添加對 hibernate-validator
的依賴
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
編碼
- 添加校驗規則
@Getter
@Setter
public class User {
@NotBlank(message = "用戶名不能爲空")
@Length(min = 6, max = 16, message = "用戶名長度必須在6-16個字符之間")
private String username;
@Past(message = "出生日期必須早於當前日期")
@JsonFormat(pattern = "yyyy-MM-dd",timezone = "GMT+8")
private LocalDate birthday;
@NotNull(message = "用戶等級不能爲空")
@Min(value = 0, message = "用戶等級最小爲0")
@Max(value = 5, message = "用戶等級最大爲5")
@Digits(integer = 1, fraction = 0, message = "用戶等級必須爲整數")
private Integer level;
}
- 使用校驗
在 Controller 接口的參數前面添加 @Valid 註解,入參注入時便會自動進行規則校驗,如果校驗成功,則執行方法體,如果校驗失敗,有兩種處理方法。
- 在參數列表的最後面添加一個 BindingResult 對象獲取校驗結果,自行組織輸出內容。
- 不使用 BindingResult 對象,則框架拋出異常,通過異常處理機制可以進行捕獲,組織輸出內容。
@RestController
@RequestMapping("/user")
@Slf4j
public class UserController {
/**
* 不使用 BindingResult 接收校驗結果,Spring 將拋出異常
*/
@PostMapping("/add1")
public User add1(@Valid @RequestBody User user) {
return user;
}
/**
* 使用 BindingResult 接收校驗結果,自行組織輸出內容
*/
@PostMapping("/add2")
public User add2(@Valid @RequestBody User user, BindingResult result) {
if (result.hasErrors()) {
StringBuilder sb = new StringBuilder();
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
sb.append(fieldError.getDefaultMessage());
sb.append(",");
}
log.debug(sb.toString());
return null;
}
return user;
}
}
- 啓動類
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
驗證結果
編寫測試用例
@RunWith(SpringRunner.class)
@WebAppConfiguration
@SpringBootTest(classes = Application.class)
public class UserTest {
private MockMvc mvc;
@Autowired
private WebApplicationContext webApplicationContext;
@Before
public void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
@Test
public void test1() throws Exception {
MvcResult mvcResult = mvc.perform(
MockMvcRequestBuilders
.post("/user/add1")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content("{\"name\":\"user1\",\"sex\":1,\"birthday\":\"2030-05-21\"}")
)
// .andExpect(status().isOk());
// .andExpect(content().string("hello"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
Assert.assertEquals(200, mvcResult.getResponse().getStatus());
}
@Test
public void test2() throws Exception {
MvcResult mvcResult = mvc.perform(
MockMvcRequestBuilders
.post("/user/add2")
.contentType(MediaType.APPLICATION_JSON_UTF8)
.content("{\"name\":\"user1\",\"sex\":1,\"birthday\":\"2030-05-21\"}")
)
// .andExpect(status().isOk());
// .andExpect(content().string("hello"))
.andDo(MockMvcResultHandlers.print())
.andReturn();
Assert.assertEquals(200, mvcResult.getResponse().getStatus());
}
}
源碼地址
本章源碼 : https://gitee.com/gongm_24/spring-boot-tutorial.git
結束語
一個安全的接口需要對每一個入參進行校驗,以保證參數合法性。
參考資料
- http://www.cnblogs.com/mr-yang-localhost/p/7812038.html
- https://blog.csdn.net/qq_22845447/article/details/84034289
- https://cloud.tencent.com/developer/article/1054194
- https://www.cnblogs.com/V1haoge/p/9953744.html
擴展
自定義校驗
- 創建一個註解,其中 @Constraint 用於指向該註冊將使用的自定義校驗類
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Constraint(validatedBy = ValueRangeValidator.class)
public @interface ValueRange {
String[] values();
String message() default "值不正確";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
- 創建自定義校驗類
public class ValueRangeValidator implements ConstraintValidator<ValueRange, Object> {
private String[] values;
@Override
public void initialize(ValueRange constraintAnnotation) {
values = constraintAnnotation.values();
}
/**
* 校驗函數
*/
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
for (String s : values) {
if (Objects.equals(s, value)) {
return true;
}
}
return false;
}
}
- 使用自定義校驗
@Getter
@Setter
public class UserBO {
/**
* 用戶名,長度在6-16個字符之間,必須參數
*/
@NotBlank(message = "用戶名不能爲空")
@Length(min = 6, max = 16, message = "用戶名長度必須在6-16個字符之間")
private String username;
/**
* 出生日期,格式爲 yyyy-MM-dd,必須爲過去的日期,不必須參數
*/
@Past(message = "出生日期必須早於當前日期")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate birthday;
/**
* 等級,整數,0-5之間,必須參數
*/
@NotNull(message = "用戶等級不能爲空")
@Min(value = 0, message = "用戶等級最小爲0")
@Max(value = 5, message = "用戶等級最大爲5")
@Digits(integer = 1, fraction = 0, message = "用戶等級必須爲整數")
private Integer level;
/**
* 限定性別的值只能是 0、1、2
*/
@ValueRange(values = {"0", "1", "2"})
private Integer sex;
}