Controller Based Exception Handling

정리공간·2022년 6월 1일
0

전역 REST Api 예외 처리
restApi 에러는 일반적인 에러가 아닌 미리 정해진 스펙으로 httpStatus code와 함께 에러를 표현해야 하므로
일반적인 에러표현과 구분되어야 한다

에러처리를 위한 DTO 생성

@Getter
@NoArgsConstructor
public class ErrorResponse {
    private int errorCode;
    private String errorMessage;
    private String detailMessage;
 
    public ErrorResponse(int errorCode, String errorMessage, String detailMessage) {
        this.errorCode = errorCode;
        this.errorMessage = errorMessage;
        this.detailMessage = detailMessage;
    }
}

추상 exception 생성

public abstract class AbstractRestException extends RuntimeException {
 
    AbstractRestException(String msg) {
        super(msg);
    }
    AbstractRestException(Throwable throwable) {
        super(throwable);
    }
 
    AbstractRestException(String msg, Throwable throwable) {
        super(msg, throwable);
    }
 
    public abstract HttpStatus getHttpStatus();
}

AbstractRestException 를 상속받은 excetion 구현.


public class ConflictException extends AbstractRestException {
    @Override
    public HttpStatus getHttpStatus() {
        return HttpStatus.CONFLICT; // 알맞은 httpStatusCode 선택
    }
 
    public ConflictException(String msg, Throwable throwable) {
        super(msg, throwable);
    }
    public ConflictException(String msg) {
        super(msg);
    }
    public ConflictException(Throwable throwable) {
        super(throwable);
    }
}

나머지 인증인가 등의 exception 추가

서비스 로직에서 생성한 exception을 던져줌

public DocnoDto saveDocno(DocnoDto docnoDto) {
    if(Boolean.TRUE.equals(this.isExistsSaveDocno(docnoDto))) {
        throw new ConflictException("The data you requested already exists in the system.");
    }
    Docno docno = docnoMapper.toEntity(docnoDto);
    return docnoMapper.toDto(docnoRepository.save(docno));
}

exception을 받아줄 controller생성

@ControllerAdvice("platform.docno.app.controller")
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ExceptionHandlerController {
 
    @ExceptionHandler(AbstractRestException.class)
    protected ResponseEntity<ErrorResponse> handleApiException(AbstractRestException e) {
        return ResponseEntity
                .status(e.getHttpStatus())
                .body(new ErrorResponse(
                        e.getHttpStatus().value(),
                        e.getHttpStatus().getReasonPhrase(),
                        e.getMessage())
                );
    }
 
    @ExceptionHandler(Exception.class)
    protected ResponseEntity<ErrorResponse> exception(Exception e) {
        return ResponseEntity
                .status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(
                        new ErrorResponse(
                                HttpStatus.INTERNAL_SERVER_ERROR.value(),
                                HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase(),
                                e.getLocalizedMessage()
                        )
                );
    }
}

@ControllerAdvice : @Controller Bean을 도와주게 한다. value값으로 범위를 선정할 수 있음.
@Order : @ControllerAdvice가 붙은 빈이 여러개일 때 우선순위를 정한다.
@Exceptionhandler : value에 해당하는 exception을 해당 메소드가 받아 처리

Controller.java

@PutMapping
public ResponseEntity<DocnoDto> saveDocno(@RequestBody DocnoDto docnoDto){
    DocnoDto rtnDto = docnoService.saveDocno(docnoDto);
    return ResponseEntity.status(HttpStatus.CREATED).body(rtnDto);
}

테스트 코드

@Test
void A01_문서번호_저장_성공() throws Exception {
    DocnoDto docnoDto = DocnoDto.builder()
            .code("TEST1")
            .name("테스트1")
            .iniCycle("mon")
            .currentNumber(1)
            .startNumber(1)
            .endNumber(9999)
            .expression("#yyyyMM#@SEQ@")
            .isUse(true)
            .tenantId(tenantId)
            .build();
 
    mockMvcPerform(put("/docnos")
            .content(new ObjectMapper().writeValueAsString(docnoDto)))
            .andExpect(status().isCreated());
}

0개의 댓글

관련 채용 정보