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.
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;
}
}Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
