Mastering RFC 7807 Problem Details in Spring Boot 3.2.5

This article explains how Spring Boot 3.2.5 implements the RFC 7807 Problem Details specification, covering the core abstractions, code examples for creating ProblemDetail and ErrorResponse objects, and practical patterns for global exception handling in Spring MVC applications.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Mastering RFC 7807 Problem Details in Spring Boot 3.2.5

Environment: Spring Boot 3.2.5

1. Introduction

Since Spring 6 and Spring Boot 3, the framework supports the “Problem Details for HTTP APIs” specification (RFC 7807). This article introduces the new feature.

See the RFC 7807 specification for details: https://www.rfc-editor.org/rfc/rfc7807.html

Problem Details Specification [RFC 7807]

The RFC defines a simple JSON and XML document format for conveying problem details to API consumers when HTTP status codes are insufficient.

Example of a bank transfer failure due to insufficient balance:

HTTP/1.1 403 Forbidden
Content-Type: application/problem+json
Content-Language: zh-CN
{
  "status": 403,
  "type": "http://www.pack.com/transfer/problemdetails/balance",
  "title": "你没有足够的余额",
  "detail": "你当前余额50但是你要转账100",
  "instance": "/accounts/transfer"
}

2. Spring Core Abstractions

2.1 ProblemDetail

The main object representing a problem detail. It contains standard fields and allows custom properties.

ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND, "message");
pd.setType(URI.create("http://www.pack.com/errors/404"));
pd.setTitle("资源不存在");

Custom fields can be added with setProperty():

pd.setProperty("custom-property", "property-value");

2.2 ErrorResponse

This interface exposes detailed HTTP error response information, including status, headers, and a body of type ProblemDetail. Most Spring MVC exceptions implement ErrorResponse, making them RFC‑compliant.

2.3 ErrorResponseException

A basic implementation of ErrorResponse that can serve as a base class for custom exceptions.

ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.INTERNAL_SERVER_ERROR, "Null Pointer Exception");
pd.setType(URI.create("http://www.pack.com/errors/npe"));
pd.setTitle("Null Pointer Exception");
// throw exception
throw new ErrorResponseException(HttpStatus.INTERNAL_SERVER_ERROR, pd, npe);

2.4 ResponseEntityExceptionHandler

A convenient base class for @ControllerAdvice that handles ErrorResponseException and renders error responses with a body.

@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
  @ExceptionHandler(Exception.class)
  public ProblemDetail handleCustomException(CustomException ex, WebRequest request) {
    ProblemDetail problemDetail = // ...
    return problemDetail;
  }
}

Note: Any @ExceptionHandler or @RequestMapping method can return ProblemDetail or ErrorResponse to produce an RFC 7807 response.

3. Practical Examples

3.1 Respond with ProblemDetail

In an exception case, create a ProblemDetail instance, populate it, and return it inside a ResponseEntity:

@GetMapping("/employees/{id}")
public ResponseEntity<Object> getEmployee(@PathVariable("id") Long id) {
  if (id < 100) {
    return ResponseEntity.ok(new Employee(id, "Pack"));
  } else {
    ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND,
        String.format("Employee id %d 不存在", id));
    pd.setType(URI.create("http://www.pack.com/errors/404"));
    pd.setTitle("资源不存在");
    pd.setProperty("extra", "xxxooo");
    return ResponseEntity.status(404).body(pd);
  }
}

When the id is greater than 100, the response body looks like:

{
  "detail": "Employee id 666 不存在",
  "extra": "xxxooo",
  "instance": "/employees/666",
  "status": 404,
  "title": "资源不存在",
  "type": "http://www.pack.com/errors/404"
}

3.2 Throw ErrorResponseException

Another way is to throw an ErrorResponseException from a controller method:

@GetMapping("/employees/{id}")
public ResponseEntity getEmployee(@PathVariable("id") Long id) {
  try {
    // ...
    throw new NullPointerException("Something was expected but it was null");
  } catch (NullPointerException npe) {
    ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND,
        String.format("Employee id %d 不存在", id));
    pd.setType(URI.create("http://www.pack.com/errors/404"));
    pd.setTitle("资源不存在");
    pd.setProperty("extra", "xxxooo");
    throw new ErrorResponseException(HttpStatus.NOT_FOUND, pd, npe);
  }
}

3.3 Global Exception Handling

By extending ResponseEntityExceptionHandler, a global handler can process custom exceptions:

@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
  @ExceptionHandler(ResourceNotFoundException.class)
  public ProblemDetail handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
    ProblemDetail pd = ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND,
        String.format("Employee id %d 不存在", id));
    pd.setType(URI.create("http://www.pack.com/errors/404"));
    pd.setTitle("资源不存在");
    pd.setProperty("extra", "xxxooo");
    return pd;
  }
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Spring BootError HandlingProblem DetailsRFC 7807
Spring Full-Stack Practical Cases
Written by

Spring Full-Stack Practical Cases

Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.