BackEnd/패캠
Exception 처리
제이드Jade
2022. 1. 22. 00:00
- validation 을 위반해서 생긴 exception을 처리해보자
- 각 exception을 잡아서 처리할 수도 있고, 통틀어서 Exception(최상위)로만 잡아줄 수 있다.
- 처리하는 방법 2가지
1) @RestControllerAdvice => 클래스를 만들어서 Global Package에 사용, 하지만 basePackageClasses를 적용하면 특정한 클래스에만 적용시킬 수 있다. / 이 안에 2가 있다.
2) @ExceptionHandler => 각 오류를 잡을 메서드에 붙이는 것
- ApiController
@Validated //컨트롤러에도 validate해서 요청 파라미터를 검증할 수 있게 함
@RestController
@RequestMapping("/api")
public class ApiController { //에러처리를 해보자!
@GetMapping("")
public User get(
@Size(min=2)
@RequestParam String name,
@Min(1)
@RequestParam Integer age){ //@RequestParam(required=false)-> 안들어와도 되고, 안들어오면 null로 셋팅됨
User user=new User();
user.setName(name);
user.setAge(age);
//파라미터로 안주면 age는 null이 되고, null값을 set하게 요청했으므로 exception 발생 => Internal Server Error가 일어남
//즉, exception 처리를 해줘야 함.
/*방법1) @ControllerAdvice => 1)Global package에 다 적용/ 2)특정 Controller로 지정가능
방법2) @ExceptionHandler => 특정 Controller에서만 됨
* 방법1 클래스를 구현하면 그 안에 방법2에도 쓰이는 @ExceptionHandler 메서드들이 있다.
방법2는 그 메서드들이 컨트롤러안에 있다. */
int a=age+10;
return user;
}
@PostMapping("")
public User post(@Valid @RequestBody User user){ //@Valid가 오류가 날때가 있음 => 걍 invalidate caches / restart
System.out.println(user);
return user;
}
//방법2 //특정 예외 처리(global보다 우선시)
// @ExceptionHandler(value = MethodArgumentNotValidException.class)
// public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
// System.out.println("api controller");
// return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
// }
}
- ApiControllerAdvice (방법1 클래스)
@RestControllerAdvice(basePackageClasses= ApiController.class) //basePackageClasses= 하면 방법1의 global이 아닌 방법2와 같은 기능
public class ApiControllerAdvice {
@ExceptionHandler(value=Exception.class) //value=처리하고 싶은 예외, 여기선 모든 예외
public ResponseEntity exception(Exception e){ //위의 value를 매개변수로 받는다.(클래스 이름 동일해야함)
System.out.println("=====================");
System.out.println(e.getClass().getName());
System.out.println(e.getLocalizedMessage());
System.out.println("=====================");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
}
@ExceptionHandler(value = MethodArgumentNotValidException.class) //post 요청객체 에러, 특정 예외 처리(위 메서드보다 우선시)
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest httpServletRequest){
BindingResult bindingResult=e.getBindingResult(); //bindingResult... 에러결과를 담아놓은 곳(?)
List<ErrorMsg> errors=new ArrayList<>();
bindingResult.getAllErrors().forEach(error -> {
FieldError field=(FieldError) error;
String fieldName=field.getField();
String message=field.getDefaultMessage();
String value=field.getRejectedValue().toString(); //입력된 (잘못된) 값
ErrorMsg errorMessage=new ErrorMsg();
errorMessage.setFieldName(fieldName);
errorMessage.setMessage(message);
errorMessage.setInvalidValue(value);
errors.add(errorMessage);
});
ErrorResponse errorResponse=new ErrorResponse();
errorResponse.setErrorList(errors);
errorResponse.setMessage("");
errorResponse.setRequestUrl(httpServletRequest.getRequestURI());
errorResponse.setResultCode(HttpStatus.BAD_REQUEST.toString());
errorResponse.setCode("FAIL");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}
@ExceptionHandler(value= MissingServletRequestParameterException.class) //get에서 받은 param으로 객체를 만들 때 객체 제약조건 위반
public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e,HttpServletRequest httpServletRequest){
List<ErrorMsg> errors=new ArrayList<>();
String fieldName=e.getParameterName(); //필드명, 위에 예외는 이거 지원 안해줘서 bindingResult 썼었는데...\
String message=e.getMessage();
ErrorMsg errorMessage=new ErrorMsg();
errorMessage.setFieldName(fieldName);
errorMessage.setMessage(message);
errors.add(errorMessage);
ErrorResponse errorResponse=new ErrorResponse();
errorResponse.setErrorList(errors);
errorResponse.setMessage("");
errorResponse.setRequestUrl(httpServletRequest.getRequestURI());
errorResponse.setResultCode(HttpStatus.BAD_REQUEST.toString());
errorResponse.setCode("FAIL");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}
@ExceptionHandler(value= ConstraintViolationException.class) // get에서 requestParam 제약조건을 위반
public ResponseEntity constraintViolationException(ConstraintViolationException e, HttpServletRequest httpServletRequest){
List<ErrorMsg> errors=new ArrayList<>();
e.getConstraintViolations().forEach(error->{ //제약 위반 요소들
//객체의 필드들을 뽑아오는게 좀 번거롭..
Stream<Path.Node> stream= StreamSupport.stream(error.getPropertyPath().spliterator(),false);
List<Path.Node> list=stream.collect(Collectors.toList());
String fieldName=list.get(list.size()-1).getName();
String message=error.getMessage();
String value=error.getInvalidValue().toString();
ErrorMsg errorMessage=new ErrorMsg();
errorMessage.setFieldName(fieldName);
errorMessage.setMessage(message);
errorMessage.setInvalidValue(value);
errors.add(errorMessage);
});
ErrorResponse errorResponse=new ErrorResponse();
errorResponse.setErrorList(errors);
errorResponse.setMessage("");
errorResponse.setRequestUrl(httpServletRequest.getRequestURI());
errorResponse.setResultCode(HttpStatus.BAD_REQUEST.toString());
errorResponse.setCode("FAIL");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse);
}
}
* 클라이언트에게 잘 짜여진 응답을 보내기 위해 만든 오류 응답 객체 (위 advice에서 이걸 body로)
- ErrorResponse
public class ErrorResponse {
private String statusCode;
private String requestUrl;
private String code;
private String message;
private String resultCode;
private List<ErrorMsg> errorList;
- Error
public class ErrorMsg {
private String fieldName;
private String message;
private String invalidValue;