728x90
TodoApiController
- 투두 앱의 CRUD 처리를 담당하는 컨트롤러 작성(설계 단계)
- Reqeust 에는 필요한 Bodyt 설계 -> Valid 작업들어가기 때문에 핸들러도 작성해준다.
@RestController
@RequestMapping("/api/todo")
class TodoApiController {
// R
@GetMapping(path = [""])
fun read(@RequestParam(required = false) index: Int?) { // 없으면 전체조회, 있으면 단건 조회, required -> 필수값 아닌지 정하기, Optional이므로 Valid할 필요 없다.
}
// C
@PostMapping(path = [""])
fun create(@Valid @RequestBody todoDto: TodoDto) { // Body설계 -> Model , 들어오는 동시에 검증(Valid)
}
// U
@PutMapping(path = [""])
fun update(@Valid @RequestBody todoDto: TodoDto) {
}
// D
@DeleteMapping(path = ["/{index}"])
fun delete(@PathVariable(name = "index") _index: Int) {
}
}
Model 설계(Body)
TodoDTO
// CRUD 하는데 필요한 Body 설계
data class TodoDto(
var index: Int? = null,
@field:NotBlank
var title: String? = null,
var description: String? = null,
@field:NotBlank
// yyyy-MM-dd HH:mm:ss
var schedule: String? = null,
var createdAt: LocalDateTime? = null,
var updatedAt: LocalDateTime? = null
) {
// TODO 이전 학습한 custom annotation으로 변경 -> Test필요
@AssertTrue(message = "yyyy-MM-dd HH:mm:ss 포맷이 맞지 않습니다.")
fun validSchedule(): Boolean {
return try {
LocalDateTime.parse(
schedule,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
) // 파싱해서 성공 -> true, 실패 -> false
true
} catch (e: Exception) {
false
}
}
}
ErrorResponse
/*
{
"result_code" : "OK",
"http_status" : "400",
"http_method" : "",
"message" : "요청이 잘못 되었습니다.",
"path" : "/api/exception/hello",
"timestamp" : "2020-10-02T13:00:00",
"error" : [
{
"field" : "_name",
"message" : "5글자 이상이여야 합니다.",
"value" : ""
}
]
}
*/
data class ErrorResponse(
@JsonProperty("reseult_code") // -> snake_case 로 변경
var resultCode: String? = null,
@JsonProperty("http_status")
var httpStatus: String? = null,
@JsonProperty("http_method")
var httpMethod: String? = null,
var message: String? = null,
var path: String? = null,
var timestamp: LocalDateTime? = null,
var error: MutableList<Error>? = null
)
data class Error(
var field: String? = null,
var message: String? = null,
var value: Any? = null
)
Model Validation Check -> TodoDtoTest(T-D-D)
class TodoDtoTest {
val validator = Validation.buildDefaultValidatorFactory().validator
@Test
fun todoDtoTest() {
val todoDto = TodoDto().apply {
this.title = "테스트"
this.description = ""
this.schedule = "2020-10-20 13:00:00"
}
val result = validator.validate(todoDto)
// # 출력해서 확인
// result.forEach {
// println(it.propertyPath.last().name)
// println(it.message)
// println(it.invalidValue)
// }
// 에러 없는 경우 True, 있는경우 False
Assertions.assertEquals(true, result.isEmpty())
}
}
ErrorHandler(TodoApiController)
TodoApiControllerAdvice
// TodoAPiController에서만 작동하는 Handler
@ControllerAdvice(basePackageClasses = [TodoApiController::class])
class TodoApiControllerAdvice {
// 옵셔널 제외하고 @RequestBody만 핸들링 하면 된다
@ExceptionHandler(MethodArgumentNotValidException::class)
fun methodArgumentNotValidException(
e: MethodArgumentNotValidException,
request: HttpServletRequest
): ResponseEntity<ErrorResponse> {
// 어떤 에러가 있는지 받기
val errors = mutableListOf<Error>()
e.bindingResult.allErrors.forEach { errorObject ->
Error().apply {
// 형변환
this.field = (errorObject as FieldError).field
this.message = errorObject.defaultMessage
this.value = errorObject.rejectedValue
}.apply {
errors.add(this)
} // for each end
}
val errorResponse = ErrorResponse().apply {
this.resultCode = "FAIL"
this.httpStatus = HttpStatus.BAD_REQUEST.value().toString()
this.httpMethod = request.method
this.message = ""
this.path = request.requestURI
this.timestamp = LocalDateTime.now()
this.error = errors
}
return ResponseEntity.badRequest().body(errorResponse)
}
}
728x90
'ETC.. > Spring' 카테고리의 다른 글
[Spring] Spring Boot로 ToDoApp만들기(투두 앱) (4) - final (0) | 2021.04.11 |
---|---|
[Spring] Spring Boot로 ToDoApp만들기(투두 앱) (3) (0) | 2021.04.11 |
[Spring] Spring Boot로 ToDoApp만들기(투두 앱) (1) (0) | 2021.04.05 |
[Spring] Spring Boot Junit 단위 테스트 설명 & 예제 (Kotlin) (0) | 2021.04.04 |
[Spring] Spring Boot 예외 처리 설명 & 예제 (Kotlin) (0) | 2021.03.17 |