[Jersey] 5. ExceptionMapper를 이용한 전역 Error Handling

2022. 5. 13. 15:24Spring/Jersey

반응형
  1. 서론
  2. ExceptionMapper 구현 클래스 정의
  3. 적용
  4. 결과

1. 서론

이전 시간에 JAX-RS에서 제공하는 WebApplicationException을 상속한 클래스를 이용하여 해당 익셉션이 발생되었을 때 400 HTTP status codee와 에러메시지를 json형태로 담아 출력되도록 설정하는 방법을 확인했습니다.

그러나, 현재 구조에서는 우리가 직접 정의한 InvalidRequestException 외의 다른 Exception이 발생했을 경우 아래와 같이 기본적으로 제공해주는 HTML 형태로 응답됩니다.

02-6

02-5


우리는 jersey를 이용해서 REST API를 만들거기 때문에, 404에러가 발생되었을 때에도 json형태로 response를 내려주고 싶습니다.

그것을 BadReqeuustException, NotFoundException, NotAllowedException를 상속한 클래스를 모두 만들어 처리하는 것은 너무 번거롭고 불편합니다.

JAX-RS에서는 전역 에러핸들링을 손쉽게 도와주는 ExceptionMapper를 제공합니다.

Spring MVC 기반의 프로젝트의 @ControllerAdvice와 @ExceptionHanlder를 이용하여 전역 Error Handling을 했습니다.
참고: [Spring Data JPA Tutorial] 12. @ControllerAdvice와 @ExceptionHandler를 이용한 전역 Error Handling


2. ExceptionMapper 구현 클래스 정의

JAX-RS에서 제공하고 있는 ExceptionMapper를 구현한 클래스를 추가합니다.

ExceptionMapper 구현 클래스는 반드시 @Provider 설정을 해줘야하며, ResourceConfig에 등록해줘야합니다.

※ ResourceConfig에 등록해서 관리되어야하는 provider들을 모두 xyz.applebox.jersey.config.provider 패키지 내에 생성하였습니다.

package xyz.applebox.jersey.config.provider;

import xyz.applebox.jersey.domain.value.BaseResponse;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;

@Provider
public class RuntimeExceptionMapper implements ExceptionMapper<RuntimeException> {

    @Override
    public Response toResponse(RuntimeException exception) {
        int status = Response.Status.INTERNAL_SERVER_ERROR.getStatusCode();
        Object entity = BaseResponse.of(exception.getMessage());

        if(exception instanceof WebApplicationException e) {
            status = e.getResponse().getStatus();
        }

        return Response.status(status)
                .type(MediaType.APPLICATION_JSON_TYPE)
                .entity(entity).build();
    }
}

REST API 상에서 발생될수 있는 익셉션들에 대해 기본 HTTP status code를 정의한 익셉션 클래스는 WebApplicationException 하위 클래스들입니다.

HTTP status code의 기본값을 500으로 설정하고, Exception 메시지를 BaseResponse에 담아 ResponseBody를 구성합니다.

만일 WebApplicationException의 하위 클래스일 경우, 그 클래스에 정의된 HTTP status code를 Response에 설정하도록 합니다.

Provider가 scan되기 위해 ResourceConfig에 등록해줍니다.
저는, xyz.applebox.jersey.config.provider 하위 Provider들을 위치시킬 예정이기 때문에 packages 로 관련 설정을 했습니다.

@Component
@ApplicationPath("/v1")
public class ResourceV1Config extends ResourceConfig {

    public ResourceV1Config() {
        packages("xyz.applebox.jersey.endpoint.v1", "xyz.applebox.jersey.config.provider");
    }

}

3. 적용

기존에 정의했었던 InvalidRequestException 클래스는 지우고 BadRequestException로 대체합니다.

@Transactional(readOnly = true)
@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;

    public List<UserValue.UserSimpleData> findAll() {
        return userRepository.findAll().stream().map(UserValue.UserSimpleData::of).collect(Collectors.toList());
    }

    public UserValue.UserData findById(long id) {
        return userRepository.findById(id).map(UserValue.UserData::of)
                .orElseThrow(() -> new BadRequestException("조회되는 User가 없습니다."));
    }

}

4. 결과

ExceptionMapper를 설정하기 전에 404 에러가 발생되었을 때 (= 존재하지 않는 api를 실행할 경우) 아래와 같이 /text/html 형식으로 response가 내려졌습니다.

02-7


ExceptionMapper를 설정한 후에는 404 에러가 발생될 때에도 json 형식으로 내려집니다.

02-8


InvalidRequestException를 BadRequestException로 대체했던 부분도 잘 작동됩니다.

02-9


※ GitHub에서 jersey 프로젝트(v1.0.4)를 다운받아 볼 수 있습니다.

728x90
반응형