A comprehensive Spring Boot-based REST error handling framework that implements standardized error responses according to RFC 9457 - Problem Details for HTTP APIs.
This project provides a unified, consistent error handling mechanism for REST APIs by implementing the Problem Details standard. It ensures that all error responses, whether from API endpoints or framework-level errors (like 404 for non-existent resources), follow a standardized JSON structure.
- RFC 9457 Compliance: Error responses follow the Problem Details for HTTP APIs specification
- Unified Error Handling: Consistent error format across all endpoints and error scenarios
- Internationalization (i18n): Localized error messages based on client's preferred locale
- Extensible Design: Pluggable providers for error types, exception mappings, and error messages
- Declarative Configuration: YAML-based configuration for error types, exception mappings, and localized messages
- Comprehensive Coverage: Handles both application exceptions and framework-level errors (404, 500, etc.)
- Standardized Format: All error responses follow the Problem Details specification
- Machine Readable: Clients can programmatically handle errors using the
typefield - Human Readable:
titleanddetailfields provide clear error descriptions - Extensible: Additional fields can be added without breaking the standard
- Automatic Localization: Error messages automatically localized based on client's
Accept-Languageheader - Intelligent Fallback: Automatic fallback to English when requested locale is unavailable
- Dynamic Discovery: Runtime discovery of available locale files from classpath
- Flexible Configuration: Simply drop new
error-messages_<locale>.ymlfiles to add language support
- Consistent Format: Same error structure for all types of errors (application, validation, framework)
- Content Type: Uses
application/problem+jsonmedia type as specified in RFC 9457 - Status Code Mapping: HTTP status codes are consistently mapped to appropriate problem types
- Provider Pattern: Easy to extend with custom error type, exception mapping and error message providers
- Declarative Configuration: Error types, messages and mappings can be defined in YAML files
- No Code Changes: New error types, messages and mappings can be added without modifying controllers
- Spring Boot 3.5.5: Web framework and auto-configuration
- Java 17: JDK Version 17
- Kotlin 1.9.25: Primary development language
- SnakeYAML: Direct YAML parsing for configuration files
- Jackson: JSON serialization with Kotlin module
- Spring Validation: Input validation support
- JUnit 5: Testing framework
The Error class implements the RFC 9457 problem details structure:
open class Error(
open val type: URI? = null, // Problem type identifier
open val status: Int? = null, // HTTP status code
open val method: String? = null, // HTTP method
open val instance: URI? = null, // URI identifying the specific occurrence
open val title: String? = null, // Human-readable summary
open var detail: String? = null, // Human-readable explanation
open val trace: String? = null // Stack trace for debugging
)Key RFC 9457 Mappings:
type: Maps to the RFC's "type" field - URI identifying the problem typestatus: Maps to the RFC's "status" field - HTTP status codeinstance: Maps to the RFC's "instance" field - URI identifying the specific occurrencetitle: Maps to the RFC's "title" field - Short, human-readable summarydetail: Maps to the RFC's "detail" field - Human-readable explanation
Extensions:
method: HTTP method that caused the errortrace: Stack trace for debugging (non-production environments)
Handles both application-level exceptions and framework-level errors (404, 500, etc.) using @RestControllerAdvice and
by extending ResponseEntityExceptionHandler class:
- Catches exceptions thrown by controllers and services
- Handles validation errors with detailed parameter information
- Provides stack traces for debugging
- Performs localization based on the client's preferred language
Handles errors from requests handled by Servlets that are outside the control of Spring Web MVC by extending Spring's BasicErrorController.
Similar to what GlobalErrorAdvice does, it basically creates an error object when a particular Servlet sends an error
response via HttpServletResponse.sendError method call.
Defines how error types are provided and resolved:
interface ErrorTypeProvider {
fun getErrorTypes(): Map<String, ErrorType>
fun getErrorType(name: String): ErrorType
fun getErrorType(statusCode: HttpStatusCode): ErrorType
fun getErrorType(status: HttpStatus): ErrorType
}Available Implementations:
-
YamlErrorTypeProvider (Default): Loads error types from
error-types.ymlconfiguration file- Provides declarative error type configuration
- Automatically active when
error-handling.provider=yamlor by default
-
DefaultErrorTypeProvider (Fallback): Provides hardcoded error types
- Contains built-in programmatically defined error types
- Active when
error-handling.provider=default
Defines how exceptions are mapped to error responses:
interface ExceptionToErrorMappingProvider {
fun getExceptionToErrorMappings(): Map<KClass<out Exception>, ExceptionToErrorMapping>
fun getExceptionToErrorMapping(ex: Exception): ExceptionToErrorMapping
}- YamlExceptionToErrorMappingProvider (Default): Loads exception to error type mappings from
error-mappings.ymlconfiguration file- Provides declarative error type configuration
- Automatically active when
error-handling.provider=yamlor by default
- DefaultExceptionToErrorMappingProvider (Fallback): Provides hardcoded exception to error type mappings:
- Contains built-in programmatically defined exception to error type mappings
- Active when
error-handling.provider=defaultExtension Point: Implement custom providers for declarative configuration
Provides localized error messages for internationalization (i18n):
interface ErrorMessageProvider {
fun getAllErrorMessages(): Map<Locale, Map<String, ErrorMessage>>
fun getErrorMessages(locale: Locale): Map<String, ErrorMessage>
fun getErrorMessage(errorType: String, locale: Locale): ErrorMessage?
fun getErrorMessageWithFallback(errorType: String, locale: Locale, defaultLocale: Locale = Locale.ENGLISH): ErrorMessage?
fun getAvailableLocales(): Set<Locale>
}Available Implementation:
- YamlErrorMessageProvider (Default): Loads localized error messages from
error-messages_<locale>.ymlfiles- Supports multiple languages and locales
- Provides automatic fallback to default locale (English)
- Discovers locale files automatically from classpath
- DefaultErrorMessageProvider (Fallback): Provides hardcoded error messages.
- Contains built-in programmatically defined error messages
- Active when
error-handling.provider=default
YAML Providers (Default):
# application.yml (or omit for default behavior)
error-handling:
provider: yamlDefault Providers (Fallback):
# application.yml
error-handling:
provider: defaultDefines available error types with their URIs and HTTP status codes:
error-types:
- error-uri: "http://www.example.com/errors/general/unknown-error"
status-code: 500
- error-uri: "http://www.example.com/errors/general/validation-error"
status-code: 400
- error-uri: "http://www.example.com/errors/foo-service/not_found"
status-code: 404
- error-uri: "http://www.example.com/errors/foo-service/foo-creation-not-allowed"
status-code: 403Maps exception types to error responses:
error-mappings:
- exception-type: com.example.errorhandling.sample.FooNotFoundException
error-type: "http://www.example.com/errors/foo-service/not_found"
- exception-type: com.example.errorhandling.sample.FooCreationNotAllowedException
error-type: "http://www.example.com/errors/foo-service/foo-creation-not-allowed"
- exception-type: java.lang.Exception
error-type: "http://www.example.com/errors/general/unknown-error"Provides localized error messages for different languages:
File Naming Convention: error-messages_<locale>.yml
- English:
error-messages_en.yml - Turkish:
error-messages_tr.yml - And more locales supported...
Example Structure:
error-messages:
- error-type: "http://www.example.com/errors/foo-service/not_found"
title: "Foo Not Found"
detail: "The requested Foo resource could not be found with the provided parameters."
- error-type: "http://www.example.com/errors/general/validation-error"
title: "Validation Error"
detail: "The provided data is invalid. Please check your input and try again."
- error-type: "http://www.example.com/errors/general/unknown-error"
title: "Unexpected Error"
detail: "An unexpected error occurred while processing your request."
- error-type: "http://www.example.com/errors/foo-service/foo-creation-not-allowed"
title: "Foo Creation Not Allowed"
detail: "You do not have the necessary permissions to create a new Foo resource."{
"type": "http://www.example.com/errors/foo-service/not_found",
"status": 404,
"method": "GET",
"instance": "/api/foo/123",
"title": "Foo Not Found",
"detail": "The requested Foo resource could not be found with the provided parameters.",
"trace": "com.example.errorhandling.sample.FooNotFoundException: Foo not found..."
}{
"type": "http://www.example.com/errors/general/validation-error",
"status": 400,
"method": "POST",
"instance": "/api/foo",
"title": "Validation Error",
"detail": "The provided data is invalid. Please check your input and try again.",
"invalidParams": [
{
"name": "email",
"reason": "must be a well-formed email address"
},
{
"name": "age",
"reason": "must be greater than or equal to 0"
}
]
}A complete demo application is available in the demo/ directory that showcases how to use this REST Error Handling framework in a real Spring Boot application.
- Live Examples: Working endpoints that demonstrate custom exceptions, validation errors, and framework errors
- Internationalization: Shows localized error messages based on
Accept-Languageheader - YAML Configuration: Sample configuration files for error types, mappings, and localized messages
- RFC 9457 Compliance: All error responses follow the Problem Details specification
-
Build and run the demo:
# Build the main library ./gradlew publishToMavenLocal # Run the demo application cd demo ./gradlew bootRun
-
Test different error scenarios:
# Custom exception curl "http://localhost:8080/foo?name=xxx" # Validation error curl "http://localhost:8080/foo" # Localized error (Turkish) curl "http://localhost:8080/foo?name=xxx" -H "Accept-Language: tr"
For detailed demo instructions and examples, see demo/README.md.
This framework provides a robust, standards-compliant foundation for REST API error handling that can be easily extended and customized for specific application needs.