전역 REST Api 예외 처리
restApi 에러는 일반적인 에러가 아닌 미리 정해진 스펙으로 httpStatus code와 함께 에러를 표현해야 하므로
일반적인 에러표현과 구분되어야 한다
@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;
}
}
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();
}
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 추가
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));
}
@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을 해당 메소드가 받아 처리
@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());
}