카테고리 없음

Spring 공식 문서 읽기 - Error Responses

Lucy Loop 2024. 8. 26. 23:29

원문: https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-ann-rest-exceptions.html

Spring Framework > Web on Servlet Stack > Spring Web MVC > Error Responses

Error Responses

REST 서비스에 대한 흔한 requirement는 오류 응답 본문에 세부 정보를 포함시키는 것이다. 스프링 프레임워크는 " Problem Details for HTTP APIs"를 지원한다 (해당 표준은 RFC 9457로,  RFC 7807를 대체했다).

이 지원에 대한 주요 추상:

  • ProblemDetail - RFC 9457 문제 세부 정보에 대한 representation. 간단한 컨테이너다. spec에 정의된 표준 필드(standard fields)와 비표준 필드(non-standard ones) 모두를 담을 수 있다.
  • ErrorResponse - HTTP에러 응답 세부 정보를 노출하는 계약. HTTP 상태, 응답 헤더, 바디를 포함한 HTTP error response details를 RFC 9457 형식으로 expose하는 contract. 이것은 예외가 HTTP 응답에 어떻게 맵핑하는지에 대한 세부사항을 캡슐화 하고 드러낼 수 있게 해준다. 모든 Spring MVC 예외는 이것을 impletent하고 있다.
  • ErrorResponseException - 기본 ErrorResponse 구현. 다른 사람들이 편리한 베이스 클래스로 사용할 수 있다. 
  • ResponseEntityExceptionHandler - 편리한 베이스 클래스. 모든 Spring MVC 예외와 모든 ErrorResponseException을 다루고, 본문과 함께 오류 응답을 렌더링 하는 @ControllerAdvice를 위한 베이스 클래스이다.

Render

RFC 9457 응답을 render하기 위해, 모든 @ExeptionHander나 모든 @RequestMapping 메서드로부터, ProblemDetail 또는 ErrorResponse를 반환할 수 있다.

다음의 과정으로 처리된다:

  • ProblemDetail의 status 속성이 HTTP status를 결정한다.
  • ProblemDetail의 instance 속성이 현재 URL경로로부터 설정된다, 만약 이미 설정된 상태가 아니라면.
  • Content negotiation을 위해, 잭슨 HttpMessageConverter는 "application/json"보다 "application/problem+json"을 선호한다, ProblemDetail을 렌더링 할 때. 그리고 호환되는 미디어 유형을 찾을 수 없는 경우, "application/problem+json" 위에서 fallback 한다. (원문: For content negotiation, the Jackson HttpMessageConverter prefers "application/problem+json" over "application/json" when rendering a ProblemDetail, and also falls back on it if no compatible media type is found. )

Spring WebFlux 예외와 모든 ErrorResponseException에 대해 RFC 9457 응답을 사용 하기 위해, ResponseEntityExceptionHandler를 extend하고,  ResponseEntityExceptionHandler를 스프링 configuration에서 @ControllerAdvice로 선언해라. 이 핸들러에는 모든 ErrorResponse 예외를 처리하는 @ExceptionHandler 메서드가 있다. ErrorResponse 예외는 모든 빌트인 웹 예외를 포함한다. 예외 처리 메서드를 더 추가하고, 보호된 메서드를 사용할 수 있다, 모든 예외를 ProblemDetail에 매핑하기 위해.

 

*Content Negotiation?

REST에서는 하나의 리소스에 대해서 여러 형태의 Representation을 가질 수 있습니다. 어떤 요청을 처리할 때 응답을 application/json 형태로 할 수도 있고, application/xml 형태로 할 수도 있습니다. 클라이언트가 요청을 전달할 때 HTTP Header 중에서 Accept라는 이름을 이용해서 원하는 응답 형태를 명시하면, 서버에서는 클라이언트가 원하는 형태로 결과를 전달합니다. 이러한 처리 과정을 Content Negotiation이라고 합니다.
출처: https://tychejin.tistory.com/179

https://spring.io/blog/2013/05/11/content-negotiation-using-spring-mvc

 

* fallback (verb: fall back -아마도...)

an alternative plan that may be used in an emergency.

프로그래밍에서도, 에러 발생 시에 원래 기능을 대신하는 기능을 fallback이라고 한다.

 

Non-Standard Fields

비표준 필드를 사용하여 RFC 9457 응답을 extend하는 방법이 두 가지 있다.

1. insert into the "properties" Map of ProblemDetail.

Jackson 라이브러리를 사용할 때, 스프링 프레임워크는 ProblemDetailJacksonMixin을 등록한다. ProblemDetailJacksonMixin은 "properties" Map이 un-랩-되고 응답에서 최상위 레벨 JSON 속성으로 렌더링 되도록 보장한다. 그리고 비슷하게, 역직렬화 중에 알 수 없는 모든 속성은 이 Map에 삽입된다.

2. 전용 비표준 속성을 추가하도록 ProblemDetail을 extend할 수 있다.

ProblemDetail의 복사 생성자가 기존 ProblemDetail로부터 하위클래스를 쉽게 만들 수 있게 해준다.

이는 중앙에서 전역적으로 수행될 수 있는데, 예를 들어 ResponseEntityExceptionHandler와 같은 @ControllerAdvice에서 일괄 처리 가능하다. ResponseEntityExceptionHandler는 한 예외의 ProblemDetail을 추가적인 비표준 필드를 가진 하위클래스로 재생산한다. (원문:This could be done centrally, e.g. from an @ControllerAdvice such as ResponseEntityExceptionHandler that re-creates the ProblemDetail of an exception into a subclass with the additional non-standard fields.)

 

* copy constructor?

객체의 복사본을 생성할 때 호출하는 생성자. (자바에서는 사용자가 직접 작성해야 함)

 

Customization and i18n

오류 응답 세부 정보를 customize 하고 internationalize 하는 것은 흔히 있는 요구 사항이다. 또한 구현 세부 정보가 노출되지 않도록 Spring MVC 예외에 대한 problem details를 사용자 지정하는 것은 좋은 practice다. 이 섹션에서는 이에 대한 지원을 설명한다.

ErrorResponse는 "type", "title", "detail", "detail" 필드에 대한 메시지 코드 매개변수를 노출한다. ResponseEntityExceptionHandler는 MessageSource를 통해 이를 해결하고, 그에 따라 해당 ProblemDetail 필드를 업데이트한다.

메시지 코드의 디폴트 전략은 다음과 같다:

  • "type": problemDetail.type.[fully qualified exception class name]
  • "title": problemDetail.title.[fully qualified exception class name]
  • "detail": problemDetail.[fully qualified exception class name][suffix]

ErrorResponse는 둘 이상의 메시지 코드를 노출할 수 있다, 전형적으로는 기본 메시지 코드에 접미사를 추가하여.

Spring MVC 예외에 대한 메시지 코드와 매개변수 리스트:

ExceptionMessage  CodeMessage  Code Arguments
AsyncRequestTimeoutException (default)  
     
ConversionNotSupportedException (default) {0} property name, 
{1} property value
HandlerMethodValidationException (default) {0} list all validation errors.
Message codes and arguments for each error are
also resolved via MessageSource.
HttpMediaTypeNotAcceptableException (default) {0} list of supported media types
HttpMediaTypeNotAcceptableException (default) + ".parseError"  
HttpMediaTypeNotSupportedException (default) {0} the media type that is not supported, 
{1} list of supported media types
HttpMediaTypeNotSupportedException (default) + ".parseError"  
HttpMessageNotReadableException (default)  
HttpMessageNotWritableException (default)  
HttpRequestMethodNotSupportedException (default) {0} the current HTTP method, 
{1} the list of supported HTTP methods
MethodArgumentNotValidException (default) {0} the list of global errors, 
{1} the list of field errors.
Message codes and arguments for each error are
also resolved via MessageSource.
MissingRequestHeaderException (default) {0} the header name
MissingServletRequestParameterException (default) {0} the request parameter name
MissingMatrixVariableException (default) {0} the matrix variable name
MissingPathVariableException (default) {0} the path variable name
MissingRequestCookieException (default) {0} the cookie name
MissingServletRequestPartException (default) {0} the part name
NoHandlerFoundException (default)  
NoResourceFoundException (default)  
TypeMismatchException (default) {0} property name, {1} property value
UnsatisfiedServletRequestParameterException (default) {0} the list of parameter conditions

Note

다른 예외와 달리 MethodArgumentValidException과 HandlerMethodValidationException의 메시지 매개변수는

MessageSourceResolvable 오류 목록에 기반한다. MessageSourceResolvable 오류는 MessageSource 리소스 번들을 통해 또한 사용자정의 될 수 있다. 관련 내용 -->  Customizing Validation Errors

 

Client Handling

WebClient를 사용할 때, 클라이언트 애플리케이션은 RestClientResponseException을 catch할 수 있다. 또는 RestTemplate를 사용한다면 클라이언트 애플리케이션은 RestClientResponseException을 catch할 수 있다. 그리고 두 예외 클래스의 getResponseBodyAs 메서드를 사용할 수 있다. getResponseBodyAs를 사용해서 오류 응답 바디를 모든 타겟 타입( ProblemDetail , ProblemDetail 의 하위클래스)으로 디코드할 수 있다.