안되는 게 더 많은 세상
API error handling을 위한 custom error handler
에러 response body는 자유롭게 구성할 수 있지만, 되도록이면 정해진 룰을 따르도록 표준화(RFC 7807) 된 response body를 구성한다.
{
"type": "/errors/incorrect-user-pass",
"title": "Incorrect username or password.",
"status": 403,
"detail": "Authentication failed due to incorrect username or password.",
"instance": "/login/log/abc123"
}
response body인 ApiExceptionResponse 클래스를 만들어준다.
@Getter
public class ApiExceptionResponse {
private String type = "ERROR";
private String title;
private int status;
private String detail;
private String instance;
public ApiExceptionResponse(HttpStatus httpStatus, String detail, String instance) {
this.title = httpStatus.getReasonPhrase();
this.status = httpStatus.value();
this.detail = detail;
this.instance = instance;
}
}
constructor에서 받아야 하는 인자는
ApiExceptionResponse를 response에 담아 보내기 위한 함수를 작성
public class ErrorHandler {
public static void handling(HttpServletResponse response, HttpStatus httpStatus, String message, String instance)
throws IOException, ServletException{
response.setCharacterEncoding("UTF-8");
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(httpStatus.value());
ObjectMapper objectMapper = new ObjectMapper();
response.getWriter().write(objectMapper.writeValueAsString(
new ApiExceptionResponse(httpStatus,message,instance)
));
}
}
instance를 만들지 않고 Errorhandler.handling()로 호출하기 위해 static 함수로 구성한다.
response.getWriter.write()
로 response body를 작성할 수 있다.
Spring에서 Object를 json으로 변환하는 것은 ObjectMapper이다.
ObjectMapper 인스턴스를 생성한 후, .writeValueAsString() 메소드를 통해 json을 작성한다.
ObjectMapper objectMapper = new ObjectMapper();
response.getWriter().write(objectMapper.writeValueAsString(
new ApiExceptionResponse(httpStatus,message,instance)
));
아래는 BasicAuthenticationFilter를 상속한 JwtAuthroizationFilter에서 사용한 ErrorHandler 예시이다.
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {
private MemberRepository memberRepository;
public JwtAuthorizationFilter(AuthenticationManager authenticationManager,
MemberRepository memberRepository
) {
super(authenticationManager);
this.memberRepository = memberRepository;
}
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
try {
String jwtHeader = request.getHeader("Authorization");
...
...
...
chain.doFilter(request, response);
}
}
catch (NullPointerException | TokenExpiredException | SignatureVerificationException e) {
ErrorHandler.handling(response, HttpStatus.UNAUTHORIZED, e.getMessage(), request.getRequestURI());
}
}
}
catch 구문에서 발생할 에러를 잡아 ErrorHandler.handling()으로 보내기
Postman으로 API 요청을 보냈을 때 나타나는 response body
{
"type": "ERROR",
"title": "Unauthorized",
"status": 401,
"detail": "The Token's Signature resulted invalid when verified using the Algorithm: HmacSHA512",
"instance": "/member/list"
}