Global하게 잡아주던 예외처리를
한 클래스에만 한정되어 지정해주게 함
<java />
package com.example.exception.advice;
import com.example.exception.controller.ApiController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice(basePackageClasses = ApiController.class) //ApiController에서만 작동하게 된다
public class ApiControllerAdvice { ////ApiController에서만 작동하게 되니까 Global에서 Api로 바꿔줌
@ExceptionHandler(value = Exception.class)
public ResponseEntity exception(Exception e){
System.out.println(e.getClass().getName()); //어떤 클래스에서 에러가 났는지 확인!
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
}
@ExceptionHandler(value= MethodArgumentNotValidException.class)
public ResponseEntity MethodArgumentNotValidException(MethodArgumentNotValidException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
이전 강의에서는 Post 예외처리만 해주었는데,
Get 요청으로도 해보자

NullPointerException이다
ApiController 코드 바꿔주자
<java />
package com.example.exception.controller;
import com.example.exception.dto.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
@RestController
@RequestMapping("/api/user")
@Validated
public class ApiController {
@GetMapping("")
public User get(
@Size(min=2)
@RequestParam String name,
@NotNull
@Min(1)
@RequestParam Integer age){
User user = new User();
user.setName(name);
user.setAge(age);
return user;
}
@PostMapping("")
public User post(@Valid @RequestBody User user){
System.out.println(user);
return user;
}
// @ExceptionHandler(value= MethodArgumentNotValidException.class)
// public ResponseEntity MethodArgumentNotValidException(MethodArgumentNotValidException e){
//
// System.out.println("api controller");
// return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
// }
}
Get 요청 보냈더니 아래와 같은 에러가 뜬다

<java />org.springframework.web.bind.MissingServletRequestParameterException
인자를 넣어주면 해결되는 오류임!

또 다른 예제
제한을 바꿔보자!
<code />
@GetMapping("")
public User get(
@Size(min=2)
@RequestParam String name,
@NotNull
@Min(1)
@RequestParam Integer age){
User user = new User();
user.setName(name);
user.setAge(age);
return user;
}

<java />javax.validation.ConstraintViolationException
ApiControllerAdvice.java에
위와 같은 에러 잡는 코드 추가
<code />
@ExceptionHandler(value= ConstraintViolationException.class)
public ResponseEntity constraintViolationException(ConstraintViolationException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
값을 아무것도 집어넣지 않았을 때
생기는 에러

<java />org.springframework.web.bind.MissingServletRequestParameterException
ApiControllerAdvice.java에
위와 같은 에러 잡는 코드 추가
<java />
@ExceptionHandler(value= MissingServletRequestParameterException.class)
public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
Post 요청에 값 안넣었을 때
오류 잡기
<code />
@ExceptionHandler(value= MethodArgumentNotValidException.class) //인자가 없을 때 발생하는 에러 잡기
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult();
bindingResult.getAllErrors().forEach(error->{
FieldError field = (FieldError) error; //형변환
String fieldName=field.getField();
String message = field.getDefaultMessage();
String value = field.getRejectedValue().toString();
System.out.println("---------------");
System.out.println(fieldName);
System.out.println(message);
System.out.println(value);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}

오른쪽 콘솔과 같이 에러 메시지가 뜬다!
Get 요청도 보냈을 때

BODY에 에러문이 뜬다
여태까지의 총 코드
<code />
package com.example.exception.advice;
import com.example.exception.controller.ApiController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
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 javax.validation.ConstraintViolationException;
import java.lang.reflect.Field;
@RestControllerAdvice(basePackageClasses = ApiController.class) //ApiController에서만 작동하게 된다
public class ApiControllerAdvice { ////ApiController에서만 작동하게 되니까 Global에서 Api로 바꿔줌
@ExceptionHandler(value = Exception.class)
public ResponseEntity exception(Exception e){
System.out.println(e.getClass().getName()); //어떤 클래스에서 에러가 났는지 확인!
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
}
@ExceptionHandler(value= MethodArgumentNotValidException.class) //인자가 없을 때 발생하는 에러 잡기
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult();
bindingResult.getAllErrors().forEach(error->{
FieldError field = (FieldError) error; //형변환
String fieldName=field.getField();
String message = field.getDefaultMessage();
String value = field.getRejectedValue().toString();
System.out.println("---------------");
System.out.println(fieldName);
System.out.println(message);
System.out.println(value);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@ExceptionHandler(value= ConstraintViolationException.class)
public ResponseEntity constraintViolationException(ConstraintViolationException e){
//어떠한 필드가 잘못되었을때의 정보를 담고 있음
e.getConstraintViolations().forEach(error ->{
System.out.println(error);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@ExceptionHandler(value= MissingServletRequestParameterException.class)
public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e){
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
Get요청에 값에 아무것도 넣지 않고 보냈을 때
에러 메시지가 안떠서 어느 함수를 거쳐서 결과를 냈는지
빨간색으로 코드를 표시하고 디버깅해보았다

맨 아래
<code />missingServletRequestParameterException
함수를 거친 것이다!
그래서 코드를 추가해주었다
<code />
@ExceptionHandler(value= MissingServletRequestParameterException.class)
public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e){
String fieldName=e.getParameterName();
String fieldType=e.getParameterType();
String invalidValue=e.getMessage();
System.out.println(fieldName);
System.out.println(fieldType);
System.out.println(invalidValue);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
결과

<java />
age
Integer
Required request parameter 'age' for method parameter type Integer is present but converted to null
콘솔에 위와 같이 잘 찍힌다
이번엔 Post요청에 아무 값도 넣지 말고 보내보자

<java />
ConstraintViolationImpl{interpolatedMessage='1 이상이어야 합니다', propertyPath=get.age, rootBeanClass=class com.example.exception.controller.ApiController, messageTemplate='{javax.validation.constraints.Min.message}'}
ConstraintViolationImpl{interpolatedMessage='크기가 2에서 2147483647 사이여야 합니다', propertyPath=get.name, rootBeanClass=class com.example.exception.controller.ApiController, messageTemplate='{javax.validation.constraints.Size.message}'}
콘솔에는 위와 같이 찍힌다


많은 값들이 있으며 가져다 쓰면 된다
값들을 가져다쓰기 위해 코드를 바꿔보자
<java />
@ExceptionHandler(value= ConstraintViolationException.class)
public ResponseEntity constraintViolationException(ConstraintViolationException e){
//어떠한 필드가 잘못되었을때의 정보를 담고 있음
e.getConstraintViolations().forEach(error ->{
String field=error.getLeafBean().toString();
String message=error.getMessage();
String value=error.getInvalidValue().toString();
System.out.println("---------------");
System.out.println(field);
System.out.println(message);
System.out.println(value);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}

필드명이 제대로 나오지 않아서 코드를 바꿔보자
<code />
package com.example.exception.advice;
import com.example.exception.controller.ApiController;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
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 javax.validation.ConstraintViolationException;
import javax.validation.Path;
import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
@RestControllerAdvice(basePackageClasses = ApiController.class) //ApiController에서만 작동하게 된다
public class ApiControllerAdvice { ////ApiController에서만 작동하게 되니까 Global에서 Api로 바꿔줌
@ExceptionHandler(value = Exception.class)
public ResponseEntity exception(Exception e){
System.out.println(e.getClass().getName()); //어떤 클래스에서 에러가 났는지 확인!
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("");
}
@ExceptionHandler(value= MethodArgumentNotValidException.class) //인자가 없을 때 발생하는 에러 잡기
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e){
BindingResult bindingResult = e.getBindingResult();
bindingResult.getAllErrors().forEach(error->{
FieldError field = (FieldError) error; //형변환
String fieldName=field.getField();
String message = field.getDefaultMessage();
String value = field.getRejectedValue().toString();
System.out.println("---------------");
System.out.println(fieldName);
System.out.println(message);
System.out.println(value);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@ExceptionHandler(value= ConstraintViolationException.class)
public ResponseEntity constraintViolationException(ConstraintViolationException e){
//어떠한 필드가 잘못되었을때의 정보를 담고 있음
e.getConstraintViolations().forEach(error ->{
Stream<Path.Node> stream = StreamSupport.stream(error.getPropertyPath().spliterator(),false);
List<Path.Node> list = stream.collect(Collectors.toList());
String field=list.get(list.size()-1).getName();
String message=error.getMessage();
String value=error.getInvalidValue().toString();
System.out.println("---------------");
System.out.println(field);
System.out.println(message);
System.out.println(value);
});
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
@ExceptionHandler(value= MissingServletRequestParameterException.class)
public ResponseEntity missingServletRequestParameterException(MissingServletRequestParameterException e){
String fieldName=e.getParameterName();
String fieldType=e.getParameterType();
String invalidValue=e.getMessage();
System.out.println(fieldName);
System.out.println(fieldType);
System.out.println(invalidValue);
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
이제 에러메시지를 예쁘게 꾸며보자!
dto 패키지에
Error.java
<code />
package com.example.exception.dto;
public class Error {
private String field;
private String message;
private String invalidValue;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getInvalidValue() {
return invalidValue;
}
public void setInvalidValue(String invalidValue) {
this.invalidValue = invalidValue;
}
}
ErrorResponse.java
<code />
package com.example.exception.dto;
public class Error {
private String field;
private String message;
private String invalidValue;
public String getField() {
return field;
}
public void setField(String field) {
this.field = field;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public String getInvalidValue() {
return invalidValue;
}
public void setInvalidValue(String invalidValue) {
this.invalidValue = invalidValue;
}
}
그리고 ApiControllerAdvice.java 코드 수정
<java />
@ExceptionHandler(value= MethodArgumentNotValidException.class) //인자가 없을 때 발생하는 에러 잡기
public ResponseEntity methodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest httpServletRequest){ //5. httpServletRequest받아오기
List<Error> errorList = new ArrayList<>(); //1. 배열 만들어주고
BindingResult bindingResult = e.getBindingResult();
bindingResult.getAllErrors().forEach(error->{
FieldError field = (FieldError) error; //형변환
String fieldName=field.getField();
String message = field.getDefaultMessage();
String value = field.getRejectedValue().toString();
System.out.println("---------------");
System.out.println(fieldName);
System.out.println(message);
System.out.println(value);
Error errorMessage = new Error(); //2. errorMessage 객체 만들어 준 뒤
errorMessage.setField(fieldName); //3. fieldName 넣어준다
errorMessage.setMessage(message);
errorMessage.setInvalidValue(value);
errorList.add(errorMessage);
});
ErrorResponse errorResponse = new ErrorResponse(); //4. errorResponse 객체 만들기
errorResponse.setErrorList(errorList);
errorResponse.setMessage("");
errorResponse.setRequestUrl(httpServletRequest.getRequestURI()); //어디를 요청했는데 에러가 났는지
errorResponse.setStatusCode(HttpStatus.BAD_REQUEST.toString());
errorResponse.setResultCode("FAIL");
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(errorResponse); //6. body에 errorResponse 심어주기
}
위, 아래에 있는 예외 처리 메서드들에도
위 코드에서 반복되는 코드들을 넣어준다
그러면 Get 요청에 인자 없이 보내면

Body에 예쁘게 결과가 나온다!
Post 요청도 잘못 보내면

역시 예쁘다
엔터프라임 프레임워크같이
컨트롤러가 4,5개가 되는 것들/주소가 10개 이상 나온다면
Validatrion 적용하고
ApiControllerAdvice같은 예외처리 자바 코드를 짜야한다.
'Spring' 카테고리의 다른 글
Spring boot - Filter (0) | 2022.06.18 |
---|---|
Spring - DTO를 사용하는 이유 (0) | 2022.06.16 |
Exception 처리 (0) | 2022.06.06 |
Custom Validation (0) | 2022.06.05 |
Validation (0) | 2022.06.01 |