728x90
Exception Annotation
@RestController
@RequestMapping("/api/exception")
@Validated
class ExceptionApiController { // 연습용 Api, 실무는 X
@GetMapping("/hello")
fun hello(): String {
val list = mutableListOf<String>()
//val temp = list[0]
return "hello"
}
@GetMapping("")
fun get(
@NotBlank
@Size(min = 2, max = 6)
@RequestParam name: String,
@Min(10)
@RequestParam age: Int
): String {
//통과된 경우만 -> print(Validated)
println(name)
println(age)
return name + " " + age
}
@PostMapping("")
fun post(@Valid @RequestBody userRequest: UserRequest): UserRequest { // bindingResult: BindingResult -> 결과 받아준다, Validation 사용하기 때문에 삭제
println(userRequest)
return userRequest
}
@ExceptionHandler(value = [MethodArgumentNotValidException::class]) // createdAt -> 형식 오류
fun methodArgumentNotValidException(
e: MethodArgumentNotValidException,
request: HttpServletRequest
): ResponseEntity<ErrorResponse> { //매개 변수 설정
val errors = mutableListOf<Error>()
e.bindingResult.allErrors.forEach { errorObject ->
val error = Error().apply {
this.field = (errorObject as FieldError).field // 형변환 field Name 불러 올 수 있음
this.message = errorObject.defaultMessage
this.value = errorObject.rejectedValue //rejected Value
}
errors.add(error)
}
// 2. ErrorResponse
val errorResponse = ErrorResponse().apply {
this.resultCode = "FAIL"
this.httpStatus = HttpStatus.BAD_REQUEST.value().toString()
this.httpMethod = request.method
this.message = "요청에 에러가 발생하였습니다."
this.path = request.requestURI.toString() // 현재 request를 찾아서 주입해 줌
this.timestamp = LocalDateTime.now()
this.errors = errors
}
// 3. Return -> ResponseEntity
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse)
}
@ExceptionHandler(value = [ConstraintViolationException::class]) // 어떤 에러를 가지는지 가져올 수 있음, 이번에는 GET, requestParam Error(Validation)
fun constraintVaolationException(
e: ConstraintViolationException,
request: HttpServletRequest
): ResponseEntity<ErrorResponse> {
// 1. 에러 분석
val errors = mutableListOf<Error>() // 에러 하나하나 넣어준다 : forEach
e.constraintViolations.forEach {
val error = Error().apply {
this.field = it.propertyPath.last().name // 마지막에 변수 이름 들어 있음
this.message = it.message
this.value = it.invalidValue
}
errors.add(error)
}
// 2. ErrorResponse
val errorResponse = ErrorResponse().apply {
this.resultCode = "FAIL"
this.httpStatus = HttpStatus.BAD_REQUEST.value().toString()
this.httpMethod = request.method
this.message = "요청에 에러가 발생하였습니다."
this.path = request.requestURI.toString() // 현재 request를 찾아서 주입해 줌
this.timestamp = LocalDateTime.now()
this.errors = errors
}
// 3. Return -> ResponseEntity
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse)
}
// if (true) {
// throw RuntimeException("강제 exception 발생") //hello method 실행되면 바로 Exception -> java.lang.RuntimeException: 강제 exception 발생
// }
// 컨트롤러 내부에 Exception Handler가 있다면 Advice타지 않고 이 곳을 탄다 , 각 장단점이 있다.
// 클래스 내부의 경우는 해당 컨트롤러 안에서만 일어나는 예외 한번에 처리 가능
// 너무 예외가 많아지면 가독성이 떨어지는 단점이 있다.
@ExceptionHandler(value = [IndexOutOfBoundsException::class])
fun indexOutOfBoundsException(e: IndexOutOfBoundsException): ResponseEntity<String> { // 200 OK, Catch 됨
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Index Error")
}
}
ExceptionApiControllerTest
@WebMvcTest // MVC와 관련된 클래스들만 로딩, MVC (단위)테스트 하기위해
@AutoConfigureMockMvc // 자동으로 MockMvc 설정
class ExceptionApiControllerTest {
// 브라우저X, 가상의 요청 -> MockMVC
@Autowired // 자동으로 MockMvc관련해서 주입을 시켜줌
lateinit var mockMvc: MockMvc
@Test
fun helloTest() {
mockMvc.perform(
get("/api/exception/hello")
).andExpect(
status().`is`(200)
).andExpect(
content().string("hello")
).andDo(print())
}
@Test
fun getTest() {
val queryParams = LinkedMultiValueMap<String, String>() // Key, Value
queryParams.add("name", "steve")
queryParams.add("age", "20")
mockMvc.perform(
get("/api/exception").queryParams(queryParams)
).andExpect(
status().isOk
).andExpect(
content().string("steve 20")
).andDo(print())
}
@Test
fun getFailTest() {
val queryParams = LinkedMultiValueMap<String, String>() // Key, Value
queryParams.add("name", "steve")
queryParams.add("age", "9")
mockMvc.perform(
get("/api/exception").queryParams(queryParams)
).andExpect(
status().isBadRequest
).andExpect(
MockMvcResultMatchers.content().contentType("application/json")
).andExpect(
jsonPath("\$.result_code").value("FAIL") // root -> 역슬래쉬
).andExpect(
jsonPath("\$.errors[0].field").value("age")
).andExpect(
jsonPath("\$.errors[0].value").value("9")
).andDo(print())
}
//Post_Test
@Test
fun postTest() {
//Json 일일이 써주는것은 힘들기 때문에 ObjectMapper 사용
val userRequest = UserRequest().apply {
this.name = "steve"
this.age = 10
this.phoneNumber = "010-1111-2222"
this.address = "경기도 성남시"
this.email = "steve@gmail.com"
this.createdAt = "2020-10-21 13:00:00"
}
//json 변환
val json = jacksonObjectMapper().writeValueAsString(userRequest)
println(json)
mockMvc.perform(
post("/api/exception")
.content(json)
.contentType("application/json")
.accept("application/json")
).andExpect(
status().isOk
).andExpect(
jsonPath("\$.name").value("steve")
).andExpect(
jsonPath("\$.age").value("10")
).andExpect(
jsonPath("\$.email").value("steve@gmail.com")
).andExpect(
jsonPath("\$.address").value("경기도 성남시")
).andDo(print())
}
@Test
fun postFailTest() {
//Json 일일이 써주는것은 힘들기 때문에 ObjectMapper 사용
val userRequest = UserRequest().apply {
this.name = "steve"
this.age = -1
this.phoneNumber = "010-1111-2222"
this.address = "경기도 성남시"
this.email = "steve@gmail.com"
this.createdAt = "2020-10-21 13:00:00"
}
//json 변환
val json = jacksonObjectMapper().writeValueAsString(userRequest)
println(json)
mockMvc.perform(
post("/api/exception") //put도 있다.
.content(json)
.contentType("application/json")
.accept("application/json")
).andExpect(
status().`is`(400)
).andDo(print())
}
}
MvcApplicationTests
@SpringBootTest
class MvcApplicationTests {
// 메소드 단위로 특정 기능 테스트 가능
@Test
fun contextLoads() {
}
}
728x90
'ETC.. > Spring' 카테고리의 다른 글
[Spring] Spring Boot로 ToDoApp만들기(투두 앱) (2) (0) | 2021.04.07 |
---|---|
[Spring] Spring Boot로 ToDoApp만들기(투두 앱) (1) (0) | 2021.04.05 |
[Spring] Spring Boot 예외 처리 설명 & 예제 (Kotlin) (0) | 2021.03.17 |
[Spring] SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet (0) | 2021.03.16 |
[Spring] Spring Boot Validation 설명 & 예제 (Kotlin) (0) | 2021.03.12 |