Spring Boot使用@Validated
檢核參數的範例如下。
範例環境:
- Spring Boot 2.3.2.RELEASE
- Maven
- Java Bean Validation
- Lombok
Spring Boot使用Java Bean Validation要在pom.xml
加入以下依賴。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
參考pom.xml
一般在Controller檢核傳入的請求參數時,可使用Java Bean Validation javax.validation.constraints
中的各種檢核annotation或自訂檢核annotation來驗證,並在方法的參數前加上@Valid
以支持檢核,但有時不同方法傳入的參數是同個bean而需要被檢核的屬性確不同,此時就可利用Spring的@Validated
對檢核進行分組(group)依不同情況進行檢核。
@Validated
透過設定class
來標注constraint annotation要檢核的group對象。下面範例設定了兩個用來group的Group1
與Group2
。因為僅用來標示group檢核的對象,所以不需要實作內容。
Group1
public interface Group1 {
}
Group2
public interface Group2 {
}
下面DemoController
的valid()
、validGroup1()
及validGroup2()
方法皆負責處理的DemoRequest
請求參數。
在方法上使用@Validated
的類別名稱前也要加上@Validated
才有效果。
valid()
沒有@Validated
,僅檢核DemoRequest
中無設定group
的屬性;
validGroup1()
有@Validated(Group1.class)
則檢核DemoRequest
中constraint annotation無設定group
及group = Group1.class
的屬性;
validGroup2()
有@Validated(Group2.class)
則檢核DemoRequest
中constraint annotation無設定group
及group = Group2.class
的屬性。
DemoController
package com.abc.demo.controller;
import com.abc.demo.controller.request.DemoRequest;
import com.abc.demo.validation.group.Group1;
import com.abc.demo.validation.group.Group2;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.ConstraintViolationException;
import javax.validation.Valid;
@Validated
@RestController
public class DemoController {
@PostMapping("/valid")
public void valid(@RequestBody @Valid DemoRequest request) {
}
@Validated(Group1.class)
@PostMapping("/valid/group1")
public void validGroup1(@RequestBody @Valid DemoRequest request) {
}
@Validated(Group2.class)
@PostMapping("/valid/group2")
public void validGroup2(@RequestBody @Valid DemoRequest request) {
}
@ExceptionHandler(ConstraintViolationException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
ResponseEntity<String> handleConstraintViolationException(ConstraintViolationException e) {
return ResponseEntity.badRequest().body(e.getMessage());
}
}
DemoRequest
為以上方法傳入的參數bean。
@Positive
沒設定group
,所以只要方法參數前有@Valid
就需要被檢核;
@Email
的group = Group1.class
,方法前有@Validated(Group1.class)
才會被檢核;
@Min
的group = Group2.class
,方法前有@Validated(Group2.class)
才會被檢。
DemoRequest
package com.abc.demo.controller.request;
import com.abc.demo.validation.group.Group1;
import com.abc.demo.validation.group.Group2;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.Email;
import javax.validation.constraints.Min;
import javax.validation.constraints.Positive;
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Data
public class DemoRequest {
@Positive(message = "id必須大於0")
private long id;
@Email(message = "信箱格式錯誤",
groups = Group1.class)
private String email;
@Min(value = 18, message = "年記不可小於18歲",
groups = Group2.class)
private int age;
}
使用DemoControllerTests
測試各個方法通過及不通過檢核的情況。
DemoControllerTests
package com.abc.demo.controller;
import com.abc.demo.controller.request.DemoRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@WebMvcTest
class DemoControllerTests {
@Autowired
private MockMvc mockMvc;
/**
* 只檢核@Positive
* id大於0。通過
*/
@Test
void valid_pass() throws Exception {
DemoRequest request = DemoRequest.builder()
.id(1L) // id大於0
.email("abc.com") // 信箱格式錯誤
.age(7) // 年齡小於18歲
.build();
String json = new ObjectMapper().writeValueAsString(request);
mockMvc.perform(
MockMvcRequestBuilders.post("/valid")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(MockMvcResultMatchers.status().isOk());
}
/**
* 只檢核@Positive
* id小於0。不通過
*/
@Test
void valid_notPass() throws Exception {
DemoRequest request = DemoRequest.builder()
.id(-1L) // id小於0
.email("abc.com") // 信箱格式錯誤
.age(7) // 年齡小於18歲
.build();
String json = new ObjectMapper().writeValueAsString(request);
mockMvc.perform(
MockMvcRequestBuilders.post("/valid")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(MockMvcResultMatchers.status().isBadRequest());
}
/**
* 檢核@@Positive,@Email
* id大於0,信箱格式正確。通過
*/
@Test
void validGroup1_pass() throws Exception {
DemoRequest request = DemoRequest.builder()
.id(1L) // id大於0
.email("john@abc.com") // 信箱格式正確
.age(7) // 年齡小於18歲
.build();
String json = new ObjectMapper().writeValueAsString(request);
mockMvc.perform(
MockMvcRequestBuilders.post("/valid/group1")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(MockMvcResultMatchers.status().isOk());
}
/**
* 檢核@@Positive,@Email
* id大於0,信箱格式錯誤。通過
*/
@Test
void validGroup1_notPass() throws Exception {
DemoRequest request = DemoRequest.builder()
.id(1L) // id大於0
.email("abc.com") // 信箱格式錯誤
.age(7) // 年齡小於18歲
.build();
String json = new ObjectMapper().writeValueAsString(request);
mockMvc.perform(
MockMvcRequestBuilders.post("/valid/group1")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(MockMvcResultMatchers.status().isBadRequest());
}
/**
* 檢核@@Positive,@Min
* id大於0,年齡大於18歲。通過
*/
@Test
void validGroup2_pass() throws Exception {
DemoRequest request = DemoRequest.builder()
.id(1L) // id大於0
.email("abc.com") // 信箱格式錯誤
.age(21) // 年齡大於18歲
.build();
String json = new ObjectMapper().writeValueAsString(request);
mockMvc.perform(
MockMvcRequestBuilders.post("/valid/group2")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(MockMvcResultMatchers.status().isOk());
}
/**
* 檢核@@Positive,@Min
* id大於0,年齡小於18歲。不通過
*/
@Test
void validGroup2_notPass() throws Exception {
DemoRequest request = DemoRequest.builder()
.id(1L) // id大於0
.email("abc.com") // 信箱格式錯誤
.age(7) // 年齡小於18歲
.build();
String json = new ObjectMapper().writeValueAsString(request);
mockMvc.perform(
MockMvcRequestBuilders.post("/valid/group2")
.contentType(MediaType.APPLICATION_JSON)
.content(json))
.andExpect(MockMvcResultMatchers.status().isBadRequest());
}
}
@Validated
也可用在service或dao層,也要與@Valid
搭配使用,但由於@Valid
僅在Controller有檢核效果,所以行為不太一樣。請參考DemoService
與測試DemoServiceTests
。
參考github
沒有留言:
張貼留言