GlobalExceptionHandler
Background
In Spring Boot, @ControllerAdvice
is used to handle exceptions globally across the entire application. Previously, in the Navida Pro backend, each microservice maintained its own GlobalExceptionHandler
, often leading to:
- Inconsistent error structures across services
- Different HTTP status codes for the same exception
- Incompatibility in how the frontend interprets error responses
Problem Example
MixedGoals BFF
returns one structure for a 500 errorBonus BFF
returns a different structure for the same error- For session expiry, one service might return 401 Unauthorized, while another returns 500 User Not Found
Due to these inconsistencies, the Frontend cannot reliably parse and act on error responses.
Objective
To solve the above issues:
- All common exceptions have been moved to a shared
GlobalExceptionHandler
inside Open Contract - A standard response structure is now used across all services
- Developers are encouraged to handle only custom exceptions at the service level
What Has Been Done
- Migrated service-level common exception handling logic to Open Contract
- Introduced
NavidaErrorResponseHelper
for standardized error responses - Designed tenant-specific and status-code-specific error messaging via config
How to Return a Standard Error Response
Step 1: Declare the Helper
private final NavidaErrorResponseHelper errorResponseHelper;
Step 2: Constructor Injection
public CMSCacheController(
CachedChallengeCMSService cachedChallengeCMSService,
LoggerFactoryUtility loggerFactoryUtility,
NavidaErrorResponseHelper errorResponseHelper,
HttpServletRequest request) {
this.cachedChallengeCMSService = cachedChallengeCMSService;
this.businessLogger = loggerFactoryUtility.getLogger(CMSCacheController.class);
this.errorResponseHelper = errorResponseHelper;
this.request = request;
}
Step 3: Use in Catch Block (With Exception)
catch (Exception e) {
log.info("Fetching goal list for AOK ID: {}", aokid);
NavidaErrorResponse errorResponse = errorResponseHelper.buildErrorResponse(e, request, HttpStatus.BAD_REQUEST);
businessLogger.error(true, "Error fetching goal list for AOK ID:{} Exception:{} {}", e, aokid, e.getMessage());
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(errorResponse);
}
Step 4: Use Without Exception Object
For situations where there's no exception but an error response is still needed (e.g., null result):
NavidaErrorResponse errorResponse = errorResponseHelper.buildErrorResponse(
new Exception("Received null response"), request, HttpStatus.BAD_REQUEST);
Standard Error Response Format
{
"errorMessage": "BadRequest.",
"errorCode": "BAD_REQUEST",
"errorType": "SystemError",
"status": 400,
"path": "/cms-cache-testing/goals",
"timestamp": "2025-06-26T17:54:37.5980658",
"traceId": "a023febdc277d4b77c08b3af2405fc63",
"source": "navida-pro-be-mixedgoals-bff-service"
}
Configurable Error Message Mapping
The errorMessage
can be derived from either the exception name or the HTTP status name. These values are tenant-specific and maintained in the properties file.
Example Configuration
aokplus.ArithmeticException=Something went wrong, please try again later.
aokplus.BadRequest=BadRequest.
aokbw.InternalServerError=Our systems currently under maintenance, please try again.
Why Support Both?
Use exception-based config when:
- You want a consistent message for a specific exception
Use status-based config when:
- You want the message to vary by HTTP status across different contexts
Example: You may want a specific message for
NullPointerException
in one scenario, and a more generalBadRequest
message in another.
Best Practices
- Use Open Contract GlobalExceptionHandler for all non-custom exceptions
- Use service-level handler only for custom/user-defined exceptions
- Always generate error responses using
NavidaErrorResponseHelper
- Log the full exception using
businessLogger.error(...)
to preserve traceability - Keep error messages configurable and tenant-specific
Adopting the Open Contract GlobalExceptionHandler
ensures:
- Consistent behavior across services
- Better supportability for frontend apps
- Centralized, configurable, and traceable error handling