-
[Spring Boot] 에러 메시지 처리 (Custom Exception)Spring Boot/기타 2022. 6. 6. 13:07반응형
1. build.grade
plugins { id 'org.springframework.boot' version '2.7.0' id 'io.spring.dependency-management' version '1.0.11.RELEASE' id 'java' } group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = '11' repositories { mavenCentral() } dependencies { // spring-boot-starter 의존성 추가 implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-web' // commons-lang3 의존성 추가 implementation 'org.apache.commons:commons-lang3:3.11' // lombok 의존성 추가 compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' // spring-boot-starter-test 의존성 추가 testImplementation 'org.springframework.boot:spring-boot-starter-test' } tasks.named('test') { useJUnitPlatform() }
2. application.yml
# Spring 설정 spring: web: locale: ko_KR resources: add-mappings: false messages: basename: message/messages # 404(Not Found) 발생 시 throw 하도록 활성화 mvc: throw-exception-if-no-handler-found: true
3. ErrorResponse 구현
ErrorResponse.java
package com.example.customexception.response; import com.example.customexception.constant.ResponseCode; import com.fasterxml.jackson.annotation.JsonProperty; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import lombok.Builder; import lombok.Getter; import lombok.Setter; import org.apache.commons.lang3.StringUtils; @Getter @Setter public class ErrorResponse { @JsonProperty("status_code") private String statusCode; @JsonProperty("message") private String message; @JsonProperty("method") private String method; @JsonProperty("path") private String path; @JsonProperty("timestamp") private String timestamp; @Builder ErrorResponse( String statusCode, String message, String method, String path, String timestamp ) { this.statusCode = statusCode; this.message = StringUtils.isNotBlank(message) ? message : ResponseCode.valueOfStatusCode(statusCode).getMessage(); this.method = method; this.path = path; this.timestamp = StringUtils.isNotBlank(timestamp) ? timestamp : LocalDateTime.now().format(DateTimeFormatter.ofPattern("YYYYMMDDHHMMSS")); } }
4. ResponseCode 구현
ResponseCode.java
package com.example.customexception.constant; import java.util.Map; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.AllArgsConstructor; import lombok.Getter; @AllArgsConstructor @Getter public enum ResponseCode { OK("200", "성공"), // Custom Exception 에러 코드 CUSTOM_EXCEPTION("800", "오류가 발생했습니다. 확인 후 다시 시도해주세요."), INVALID_REQUEST("801", "유효하지 않은 요청입니다."), FORBIDDEN_REQUEST("802", "허용되지 않은 요청입니다."), DUPLICATED_REQUEST("803", "중복된 요청입니다."), NOT_FOUND("804", "존재하지 않는 정보입니다."), // Exception Handler 에러 코드 INTERNAL_SERVER_ERROR("900", "내부 오류가 발생했습니다. 확인 후 다시 시도해주세요."), METHOD_ARGUMENT_NOT_VALID("901", "파라미터가 유효하지 않습니다."), MISSING_SERVLET_REQUEST_PARAMETER("902", "필수 파라미터가 누락되었습니다."), CONSTRAINT_VIOLATION("903", "파라미터 유효성 검사에 실패했습니다."), METHOD_ARGUMENT_TYPE_MISMATCH("904", "파라미터 타입이 올바르지 않습니다."), HTTP_MESSAGE_NOT_READABLE_EXCEPTION("905", "읽을 수 있는 요청 정보가 없습니다."), NO_HANDLER_FOUND("906", "요청한 URL을 찾을 수 없습니다."), HTTP_REQUEST_METHOD_NOT_SUPPORTED("907", "지원하지 않는 메서드입니다."), HTTP_MEDIA_TYPE_NOT_SUPPORTED("908", "지원되지 않는 미디어 타입입니다."); private final String code; private final String message; private static final Map<String, ResponseCode> BY_STATUS_CODE = Stream.of(values()) .collect(Collectors.toMap(ResponseCode::getCode, Function.identity())); private static final Map<String, ResponseCode> BY_MESSAGE = Stream.of(values()) .collect(Collectors.toMap(ResponseCode::getMessage, Function.identity())); public static ResponseCode valueOfStatusCode(String statusCode) { return BY_STATUS_CODE.get(statusCode); } public static ResponseCode valueOfMessage(String message) { return BY_MESSAGE.get(message); } }
5. ErrorException 구현
ErrorException.java
package com.example.customexception.exception; import com.example.customexception.constant.ResponseCode; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; @AllArgsConstructor @NoArgsConstructor @Getter public class ErrorException extends RuntimeException { private ResponseCode responseCode; private String message; public ErrorException(ResponseCode rtaStatusCode) { super(); this.responseCode = rtaStatusCode; this.message = rtaStatusCode.getMessage(); } public ErrorException(String message) { super(); this.responseCode = ResponseCode.CUSTOM_EXCEPTION; this.message = message; } }
6. ErrorExceptionDto 구현
ErrorExceptionDto.java
package com.example.customexception.dto; import com.fasterxml.jackson.annotation.JsonProperty; import javax.validation.constraints.AssertFalse; import javax.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import lombok.ToString; public class ErrorExceptionDto { @Getter @Setter @Builder @AllArgsConstructor @NoArgsConstructor @ToString public static class Request { @JsonProperty("id") private Long id; @NotBlank @JsonProperty("title") private String title; @NotBlank @JsonProperty("description") private String description; @AssertFalse @JsonProperty("completed") private Boolean completed; } @Getter @Setter @AllArgsConstructor @NoArgsConstructor @ToString public class Response { @JsonProperty("id") private Long id; @JsonProperty("title") private String titile; @JsonProperty("description") private String description; @JsonProperty("completed") private Boolean completed; } }
7. ExceptionAspect 구현
ExceptionAspect.java
package com.example.customexception.advice; import com.example.customexception.constant.ResponseCode; import com.example.customexception.exception.ErrorException; import com.example.customexception.response.ErrorResponse; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.validation.ConstraintViolationException; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.util.ObjectUtils; import org.springframework.validation.BindingResult; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.servlet.NoHandlerFoundException; @Slf4j @RestControllerAdvice public class ExceptionAspect { /** * ExceptionHandler - Exception */ @ExceptionHandler(Exception.class) public ResponseEntity<ErrorResponse> handleException( HttpServletRequest request, Exception e) { log.error("handleException : {}", e); return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body( ErrorResponse.builder() .statusCode(ResponseCode.INTERNAL_SERVER_ERROR.getCode()) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - Custom Exception */ @ExceptionHandler(ErrorException.class) public ResponseEntity<ErrorResponse> handleRtaException( HttpServletRequest request, ErrorException e) { log.error("handleRtaException : {}", e); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( ErrorResponse.builder() .statusCode(e.getResponseCode().getCode()) .method(request.getMethod()) .message(e.getMessage()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - MethodArgumentNotValidException */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseEntity<ErrorResponse> handleMethodArgumentNotValidException( HttpServletRequest request, MethodArgumentNotValidException e) { log.error("handleMethodArgumentNotValidException : {}", e); String message = ""; List<String> messages = new ArrayList<>(); BindingResult bindingResult = e.getBindingResult(); for (ObjectError error : bindingResult.getGlobalErrors()) { messages.add( camelToSnake(error.getObjectName()) + ":" + error.getDefaultMessage()); } for (FieldError error : bindingResult.getFieldErrors()) { messages.add(camelToSnake(error.getField()) + ":" + error.getDefaultMessage()); } if (!ObjectUtils.isEmpty(messages)) { message = String.join(", ", messages); } return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( ErrorResponse.builder() .statusCode(ResponseCode.METHOD_ARGUMENT_NOT_VALID.getCode()) .message(message) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - MissingServletRequestParameterException */ @ExceptionHandler(MissingServletRequestParameterException.class) public ResponseEntity<ErrorResponse> handleMissingServletRequestParameterException( HttpServletRequest request, MissingServletRequestParameterException e) { log.error("handleMissingServletRequestParameterException : {}", e); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( ErrorResponse.builder() .statusCode(ResponseCode.MISSING_SERVLET_REQUEST_PARAMETER.getCode()) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - ConstraintViolationException */ @ExceptionHandler(ConstraintViolationException.class) public ResponseEntity<ErrorResponse> handleConstraintViolationException( HttpServletRequest request, ConstraintViolationException e) { log.error("handleConstraintViolationException : {}", e); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( ErrorResponse.builder() .statusCode(ResponseCode.CONSTRAINT_VIOLATION.getCode()) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - MethodArgumentTypeMismatchException */ @ExceptionHandler(MethodArgumentTypeMismatchException.class) public ResponseEntity<ErrorResponse> handleMethodArgumentTypeMismatchException( HttpServletRequest request, MethodArgumentTypeMismatchException e) { log.error("handleMethodArgumentTypeMismatchException : {}", e); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( ErrorResponse.builder() .statusCode(ResponseCode.METHOD_ARGUMENT_TYPE_MISMATCH.getCode()) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - HttpMessageNotReadableException */ @ExceptionHandler(HttpMessageNotReadableException.class) public ResponseEntity<ErrorResponse> handleHttpMessageNotReadableException( HttpServletRequest request, HttpMessageNotReadableException e) { log.error("handleHttpMessageNotReadableException : {}", e); return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( ErrorResponse.builder() .statusCode(ResponseCode.HTTP_MESSAGE_NOT_READABLE_EXCEPTION.getCode()) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - NoHandlerFoundException */ @ExceptionHandler(NoHandlerFoundException.class) public ResponseEntity<ErrorResponse> handleNoHandlerFoundException( HttpServletRequest request, NoHandlerFoundException e) { log.error("handleNoHandlerFoundException : {}", e); return ResponseEntity.status(HttpStatus.NOT_FOUND).body( ErrorResponse.builder() .statusCode(ResponseCode.NO_HANDLER_FOUND.getCode()) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - HttpRequestMethodNotSupportedException */ @ExceptionHandler(HttpRequestMethodNotSupportedException.class) public ResponseEntity<ErrorResponse> handleHttpRequestMethodNotSupportedException( HttpServletRequest request, HttpRequestMethodNotSupportedException e) { log.error("handleHttpRequestMethodNotSupportedException : {}", e); return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body( ErrorResponse.builder() .statusCode(ResponseCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getCode()) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * ExceptionHandler - HttpMediaTypeNotSupportedException */ @ExceptionHandler(HttpMediaTypeNotSupportedException.class) public ResponseEntity<ErrorResponse> handleHttpMediaTypeNotSupportedException( HttpServletRequest request, HttpMediaTypeNotSupportedException e) { log.error("handleHttpMediaTypeNotSupportedException : {}", e); return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE).body( ErrorResponse.builder() .statusCode(ResponseCode.HTTP_MEDIA_TYPE_NOT_SUPPORTED.getCode()) .method(request.getMethod()) .path(request.getRequestURI()) .build() ); } /** * Camal Case를 Snake Case로 변환 */ public static String camelToSnake(String str) { StringBuilder sb = new StringBuilder(); char c = str.charAt(0); sb.append(Character.toLowerCase(c)); for (int i = 1; i < str.length(); i++) { char ch = str.charAt(i); if (Character.isUpperCase(ch)) { sb.append('_'); } sb.append(Character.toLowerCase(ch)); } return sb.toString(); } }
8. ExceptionController 구현
ExceptionController.java
package com.example.customexception.controller; import com.example.customexception.constant.ResponseCode; import com.example.customexception.dto.ErrorExceptionDto; import com.example.customexception.exception.ErrorException; import java.util.Set; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import javax.validation.Valid; import javax.validation.Validation; import javax.validation.Validator; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; @RestController public class ExceptionController { /** * RuntimeException */ @GetMapping("/api/exception01") public String exception01() { throw new RuntimeException("RuntimeException 발생"); } /** * ErrorException(ResponseCode rtaStatusCode) */ @GetMapping("/api/exception02") public String exception02() { throw new ErrorException(ResponseCode.CUSTOM_EXCEPTION); } /** * ErrorException(String message) */ @GetMapping("/api/exception03") public String exception03() { throw new ErrorException("ErrorException 발생"); } /** * MethodArgumentNotvalidException */ @PostMapping("/api/methodArgumentNotvalidException") public String methodArgumentNotValidException( @Valid @RequestBody ErrorExceptionDto.Request errorRequest ) { return "methodArgumentNotvalidException"; } /** * MethodArgumentNotvalidException */ @GetMapping("/api/missingServletRequestParameterException") public String missingServletRequestParameterException( @RequestParam("id") Long id ) { return "missingServletRequestParameterException"; } /** * MethodArgumentNotvalidException */ @PostMapping("/api/constraintViolationException") public String constraintViolationException( @RequestBody ErrorExceptionDto.Request errorRequest ) { Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); Set<ConstraintViolation<ErrorExceptionDto.Request>> violations = validator.validate(errorRequest); if (!violations.isEmpty()) { throw new ConstraintViolationException(violations); } return "constraintViolationException"; } /** * MethodArgumentNotvalidException */ @GetMapping("/api/methodArgumentTypeMismatchException") public String methodArgumentTypeMismatchException( @RequestParam("id") Long id ) { return "methodArgumentTypeMismatchException"; } /** * HttpMessageNotReadableException */ @PostMapping("/api/httpMessageNotReadableException") public String httpMessageNotReadableException( @Valid @RequestBody ErrorExceptionDto.Request errorRequest ) { return "httpMessageNotReadableException"; } /** * NoHandlerFoundException */ @DeleteMapping("/api/noHandlerFoundException") public String noHandlerFoundException( @RequestParam("id") Long id ) { return "noHandlerFoundException"; } /** * HttpRequestMethodNotSupportedException */ @PostMapping("/api/httpRequestMethodNotSupportedException") public String httpRequestMethodNotSupportedException( @Valid @RequestBody ErrorExceptionDto.Request errorRequest ) { return "httpRequestMethodNotSupportedException"; } /** * HttpMediaTypeNotSupportedException */ @PostMapping("/api/httpMediaTypeNotSupportedException") public String httpMediaTypeNotSupportedException( @Valid @RequestBody ErrorExceptionDto.Request errorRequest ) { return "httpMediaTypeNotSupportedException"; } }
9. ExceptionControllerTest 구현
ExceptionControllerTest.java
package com.example.customexception.controller; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import com.example.customexception.constant.ResponseCode; import com.example.customexception.dto.ErrorExceptionDto; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.ResultActions; import org.springframework.util.LinkedMultiValueMap; @TestMethodOrder(MethodOrderer.OrderAnnotation.class) @AutoConfigureMockMvc @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Disabled class ExceptionControllerTest { @Autowired MockMvc mockMvc; @Autowired ObjectMapper objectMapper; @Order(1) @DisplayName("exception01_조회_에러 코드:900") @Test void testException01() throws Exception { // Given & When String url = "/api/exception01"; ResultActions resultActions = mockMvc.perform(get(url)); // Then resultActions .andExpect(status().is5xxServerError()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.INTERNAL_SERVER_ERROR.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.INTERNAL_SERVER_ERROR.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(2) @DisplayName("exception02_조회_에러 코드:800") @Test void testException02() throws Exception { // Given & When String url = "/api/exception02"; ResultActions resultActions = mockMvc.perform(get(url)); // Then resultActions .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.CUSTOM_EXCEPTION.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.CUSTOM_EXCEPTION.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(3) @DisplayName("exception03_조회_에러 코드:800") @Test void testException03() throws Exception { // Given & When String url = "/api/exception03"; ResultActions resultActions = mockMvc.perform(get(url)); // Then resultActions .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.CUSTOM_EXCEPTION.getCode()) ) .andExpect( jsonPath("$.message") .value("ErrorException 발생") ) .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(4) @DisplayName("methodArgumentNotValidException_조회_에러 코드:901") @Test void testMethodArgumentNotValidException() throws Exception { // Given & When String url = "/api/methodArgumentNotvalidException"; ErrorExceptionDto.Request errorRequest = ErrorExceptionDto.Request.builder() .title("") .description("") .completed(false) .build(); ResultActions resultActions = mockMvc.perform( post(url) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(errorRequest)) ); // Then resultActions .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.METHOD_ARGUMENT_NOT_VALID.getCode()) ) .andExpect(jsonPath("$.message").isNotEmpty()) .andExpect(jsonPath("$.method").value(HttpMethod.POST.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(5) @DisplayName("missingServletRequestParameterException_조회_에러 코드:902") @Test void testMissingServletRequestParameterException() throws Exception { // Given & When String url = "/api/missingServletRequestParameterException"; LinkedMultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("missingId", "1"); ResultActions resultActions = mockMvc.perform( get(url) .queryParams(queryParams) ); // Then resultActions .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.MISSING_SERVLET_REQUEST_PARAMETER.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.MISSING_SERVLET_REQUEST_PARAMETER.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(6) @DisplayName("constraintViolationException 코드:903") @Test void testConstraintViolationException() throws Exception { // Given & When String url = "/api/constraintViolationException"; ErrorExceptionDto.Request errorExceptionDto = ErrorExceptionDto.Request.builder() .title("") .description("") .completed(false) .build(); ResultActions resultActions = mockMvc.perform( post(url) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(errorExceptionDto)) ); // Then resultActions .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.CONSTRAINT_VIOLATION.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.CONSTRAINT_VIOLATION.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.POST.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(7) @DisplayName("methodArgumentTypeMismatchException_조회_에러 코드:904") @Test void testMethodArgumentTypeMismatchException() throws Exception { // Given & When String url = "/api/methodArgumentTypeMismatchException"; LinkedMultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("id", "test"); ResultActions resultActions = mockMvc.perform( get(url) .queryParams(queryParams) ); // Then resultActions .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.METHOD_ARGUMENT_TYPE_MISMATCH.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.METHOD_ARGUMENT_TYPE_MISMATCH.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(8) @DisplayName("httpMessageNotReadableException 코드:905") @Test void testHttpMessageNotReadableException() throws Exception { // Given & When String url = "/api/httpMessageNotReadableException"; ResultActions resultActions = mockMvc.perform( post(url) .contentType(MediaType.APPLICATION_JSON) ); // Then resultActions .andExpect(status().isBadRequest()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.HTTP_MESSAGE_NOT_READABLE_EXCEPTION.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.HTTP_MESSAGE_NOT_READABLE_EXCEPTION.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.POST.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(9) @DisplayName("noHandlerFoundException 코드:906") @Test void testNoHandlerFoundException() throws Exception { // Given & When String url = "/api/noHandlerFoundExceptionFailed"; LinkedMultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("id", "1"); ResultActions resultActions = mockMvc.perform( get(url) .queryParams(queryParams) ); // Then resultActions .andExpect(status().isNotFound()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.NO_HANDLER_FOUND.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.NO_HANDLER_FOUND.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(10) @DisplayName("httpRequestMethodNotSupportedException 코드:907") @Test void testHttpRequestMethodNotSupportedException() throws Exception { // Given & When String url = "/api/httpRequestMethodNotSupportedException"; LinkedMultiValueMap queryParams = new LinkedMultiValueMap<>(); queryParams.add("id", "1"); ResultActions resultActions = mockMvc.perform( get(url) .queryParams(queryParams) ); // Then resultActions .andExpect(status().isMethodNotAllowed()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.HTTP_REQUEST_METHOD_NOT_SUPPORTED.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.GET.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } @Order(11) @DisplayName("httpMediaTypeNotSupportedException 코드:908") @Test void testHttpMediaTypeNotSupportedException() throws Exception { // Given & When String url = "/api/httpMediaTypeNotSupportedException"; ErrorExceptionDto.Request errorExceptionDto = ErrorExceptionDto.Request.builder() .title("Spring Boot") .description("description00") .completed(false) .build(); ResultActions resultActions = mockMvc.perform( post(url) .contentType(MediaType.TEXT_PLAIN) .content(objectMapper.writeValueAsString(errorExceptionDto)) ); // Then resultActions .andExpect(status().isUnsupportedMediaType()) .andExpect(content().contentType(MediaType.APPLICATION_JSON)) .andExpect( jsonPath("$.status_code") .value(ResponseCode.HTTP_MEDIA_TYPE_NOT_SUPPORTED.getCode()) ) .andExpect( jsonPath("$.message") .value(ResponseCode.HTTP_MEDIA_TYPE_NOT_SUPPORTED.getMessage()) ) .andExpect(jsonPath("$.method").value(HttpMethod.POST.toString())) .andExpect(jsonPath("$.path").value(url)) .andExpect(jsonPath("$.timestamp").isNotEmpty()) .andDo(print()); } }
소스 코드는 Github Repository - https://github.com/tychejin1218/custom-exception 를 참조하세요.
참고 : https://www.baeldung.com/global-error-handler-in-a-spring-rest-api
반응형'Spring Boot > 기타' 카테고리의 다른 글
[Spring Boot] Amazon S3로 파일 업로드 및 삭제 (0) 2023.01.27 [Spring boot] Database가 Replication일 때 DataSource 설정 (2) 2023.01.17 [Spring Boot] Spring Data JPA + QueryDSL 설정 (2) 2023.01.15 [Spring Boot] 유효성 검사 처리 (Custom Validation) (0) 2022.06.06 [Spring Boot] MockMvc 사용 시 한글이 깨지는 현상 (0) 2021.08.29