Error Responses

A common requirement for REST services is to include details in the body of error responses. The Spring Framework supports the "Problem Details for HTTP APIs" specification, RFC 7807.

The following are the main abstractions for this support:

  • ProblemDetail — representation for an RFC 7807 problem detail; a simple container for both standard fields defined in the spec, and for non-standard ones.

  • ErrorResponse — contract to expose HTTP error response details including HTTP status, response headers, and a body in the format of RFC 7807; this allows exceptions to encapsulate and expose the details of how they map to an HTTP response. All Spring MVC exceptions implement this.

  • ErrorResponseException — basic ErrorResponse implementation that others can use as a convenient base class.

  • ResponseEntityExceptionHandler — convenient base class for an @ControllerAdvice that handles all Spring MVC exceptions, and any ErrorResponseException, and renders an error response with a body.

Render

You can return ProblemDetail or ErrorResponse from any @ExceptionHandler or from any @RequestMapping method to render an RFC 7807 response. This is processed as follows:

  • The status property of ProblemDetail determines the HTTP status.

  • The instance property of ProblemDetail is set from the current URL path, if not already set.

  • 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.

To enable RFC 7807 responses for Spring WebFlux exceptions and for any ErrorResponseException, extend ResponseEntityExceptionHandler and declare it as an @ControllerAdvice in Spring configuration. The handler has an @ExceptionHandler method that handles any ErrorResponse exception, which includes all built-in web exceptions. You can add more exception handling methods, and use a protected method to map any exception to a ProblemDetail.

Non-Standard Fields

You can extend an RFC 7807 response with non-standard fields in one of two ways.

One, insert into the "properties" Map of ProblemDetail. When using the Jackson library, the Spring Framework registers ProblemDetailJacksonMixin that ensures this "properties" Map is unwrapped and rendered as top level JSON properties in the response, and likewise any unknown property during deserialization is inserted into this Map.

You can also extend ProblemDetail to add dedicated non-standard properties. The copy constructor in ProblemDetail allows a subclass to make it easy to be created from an existing 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.

Customization and i18n

It is a common requirement to customize and internationalize error response details. It is also good practice to customize the problem details for Spring MVC exceptions to avoid revealing implementation details. This section describes the support for that.

An ErrorResponse exposes message codes for "type", "title", and "detail", as well as message code arguments for the "detail" field. ResponseEntityExceptionHandler resolves these through a MessageSource and updates the corresponding ProblemDetail fields accordingly.

The default strategy for message codes is as follows:

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

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

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

An ErrorResponse may expose more than one message code, typically adding a suffix to the default message code. The table below lists message codes, and arguments for Spring MVC exceptions:

Exception Message Code Message 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

Unlike other exceptions, the message arguments for MethodArgumentValidException and HandlerMethodValidationException are baed on a list of MessageSourceResolvable errors that can also be customized through a MessageSource resource bundle. See Customizing Validation Errors for more details.

Client Handling

A client application can catch WebClientResponseException, when using the WebClient, or RestClientResponseException when using the RestTemplate, and use their getResponseBodyAs methods to decode the error response body to any target type such as ProblemDetail, or a subclass of ProblemDetail.