Exception Handling
About
Exception handling in RestTemplate is crucial to ensure robust communication between services, especially in distributed systems where network failures, downstream service issues, or unexpected responses are common.
Spring’s RestTemplate throws runtime exceptions (unchecked) to notify the developer when an error occurs during HTTP interaction. By default, Spring uses the DefaultResponseErrorHandler which throws exceptions for non-2xx status codes.
Without proper handling, errors like timeouts, 404s, or 500s can crash our service or cause undefined behavior. That’s why it's essential to implement systematic exception handling that can recover gracefully, log appropriately, and surface actionable information.
Types of Exceptions Thrown by RestTemplate
Exception Type
Description
Typical Scenario
HttpClientErrorException
Thrown for HTTP status codes in the 4xx range.
404 Not Found, 400 Bad Request, 401 Unauthorized
HttpServerErrorException
Thrown for 5xx errors from the server.
500 Internal Server Error, 503 Service Unavailable
ResourceAccessException
Thrown when connection errors occur — DNS issue, timeout, no network.
Network unreachable, service down
UnknownHttpStatusCodeException
Thrown for unrecognized or custom status codes not part of standard HTTP.
Non-standard HTTP response codes
RestClientException (base class)
Base class for all RestTemplate client exceptions.
Catch-all for unknown/unexpected errors
Default Behavior (DefaultResponseErrorHandler)
By default, Spring's RestTemplate uses DefaultResponseErrorHandler, which:
Throws exceptions on all 4xx and 5xx HTTP responses.
Does not throw exceptions on 2xx or 3xx responses.
Does not handle the error body unless parsed manually.
Handling Exception
Wrap RestTemplate calls in try-catch blocks
RestTemplate calls in try-catch blocksThis is the most basic but explicit form of exception handling. We surround our RestTemplate calls with try-catch blocks where each catch clause targets a specific category of exceptions such as client errors (4xx), server errors (5xx), or connection timeouts.
Centralize Handling Using a Utility/Adapter Layer
Instead of littering try-catch blocks across the codebase, enterprises abstract RestTemplate logic into a shared HttpClientHelper, RestTemplateAdapter, or HttpIntegrationService. This class wraps all RestTemplate interactions and handles common concerns like retries, error transformation, timeouts, logging, and telemetry.
Use ResponseErrorHandler for Global Error Handling
ResponseErrorHandler for Global Error HandlingSpring's RestTemplate allows us to plug in a custom ResponseErrorHandler that can intercept and process HTTP errors globally before they reach our business logic. This means we don’t need to handle HttpStatusCodeException manually every time.
Registering the Handler
Handling Deserialization Errors
When using RestTemplate, after a successful HTTP response (e.g., 200 OK), the response body is typically converted (deserialized) into a Java object using Jackson (or another configured message converter). If the response payload doesn't match the expected Java structure, deserialization fails, usually throwing a HttpMessageConversionException (like JsonMappingException, MismatchedInputException, etc.).
These errors are runtime failures and not caught by the usual ResponseErrorHandler, since they occur after the response is successfully received, but before the result is returned to the caller.
Strategies to Handle Deserialization Errors
1. Wrap RestTemplate Calls and Catch Jackson Exceptions
This is the first line of defense. Deserialization errors can be caught and handled using try-catch.
Key Exceptions to Catch
HttpMessageNotReadableException
Thrown when the response body can’t be parsed (e.g., bad JSON)
JsonMappingException
Thrown by Jackson when structure mismatches (e.g., missing fields, type errors)
MismatchedInputException
Specific Jackson subtype when input doesn’t match target type
2. Define Resilient DTOs
To make DTOs less sensitive to upstream changes:
Mark all fields as optional using
@JsonInclude(JsonInclude.Include.NON_NULL)Use
@JsonIgnoreProperties(ignoreUnknown = true)on class levelAvoid using primitives (
int,boolean) if the fields are optionalConsider using
@JsonProperty(required = false)if necessary
3. Log the Raw Response on Failure
Logging the raw JSON response helps debugging deserialization issues.
We can read raw response manually if needed:
This technique is also useful when the response is partially malformed but salvageable.
4. Implement Custom Error-Resilient Deserializer
For advanced cases, we can write custom Jackson deserializers:
Then register it:
5. Fallback Strategy: Fallback DTO or Raw Map
If payloads are highly dynamic or not fully trusted:
This allows inspection of the payload without schema enforcement. We can then map it to DTO manually or partially.
Last updated