-
Notifications
You must be signed in to change notification settings - Fork 38.8k
Open
Labels
in: webIssues in web modules (web, webmvc, webflux, websocket)Issues in web modules (web, webmvc, webflux, websocket)type: documentationA documentation taskA documentation task
Milestone
Description
I face a challenge when migrating from RestTemplate to RestClient. The documentation mentions the changes in calls, but I have specific exception handling dealing that I have to return to the client (e.g. timeouts, invalid urls, invalid ssl certificate). These corner cases are difficult to test during this migration, and I am afraid I might miss some new ones.
Could you provide a migration guide for the exceptions thrown by RestTemplate, to the exceptions thrown by RestClient? Has anything changed?
- Are RestTemplate's
HttpStatusCodeException
andResourceAccessException
still thrown by RestClient? - Are the underlying exceptions still retrievable?
My project started with Spring Boot 1.5.4 and is now on 3.5.x, so I might also have missed some changes in the meantime.
My code looks mostly like this:
record HttpRequest<T>(HttpMethod method, String url, Map<String, String> headers, T body) {}
record HttpResponse(HttpStatusCode httpStatusCode, Map<String, String> headers, String body) {}
public HttpResponse sendRequest(HttpRequest<?> request)
{
try
{
HttpHeaders headers = new HttpHeaders();
request.getHeaders().forEach(headers::set);
HttpEntity<?> requestEntity = new HttpEntity<>(request.getBody(), headers);
ResponseEntity<String> response = restTemplate.exchange(request.getUrl(), request.getMethod(), requestEntity, String.class);
return new HttpResponse(response.getStatusCode(), response.getHeaders().toSingleValueMap(), response.getBody());
}
catch (HttpStatusCodeException e)
{
HttpStatusCode status = e.getStatusCode();
switch (status.value())
{
case 400: { /*...*/}
case 401: { /*...*/}
//etc.
}
return new HttpResponse(status, e.getResponseHeaders().toSingleValueMap(), e.getResponseBodyAsString());
}
catch (ResourceAccessException e)
{
Class<?> exceptionClass = e.getRootCause() != null ? e.getRootCause().getClass() : null;
final HttpStatus httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
ObjectNode body = objectMapper.createObjectNode();
if (isA(java.net.UnknownHostException.class, exceptionClass))
{
// UnknownHostException - Invalid URL (DNS resolve failed)
body.put("reason", "Invalid URL");
}
else if (isA(java.net.ConnectException.class, exceptionClass))
{
// ConnectException - Connection refused
body.put("reason", "Server not connecting. Connection refused.");
}
else if (isA(org.apache.hc.client5.http.ConnectTimeoutException.class, exceptionClass))
{
// ConnectTimeoutException - Server not connecting within timeout
body.put("reason", "Server not connecting in a timely fashion");
body.put("timeout", restProperties.getConnectionRequestTimeout());
}
else if (isA(java.net.SocketTimeoutException.class, exceptionClass))
{
// SocketTimeoutException - Server not responding within timeout
body.put("reason", "Server not responding in a timely fashion");
body.put("timeout", restProperties.getSocketTimeout());
}
else if (isA(javax.net.ssl.SSLPeerUnverifiedException.class, exceptionClass))
{
// SSLPeerUnverifiedException - Certificate not valid for this domain
body.put("reason", "Certificate not valid for this domain");
}
else
{
// Other
body.put("reason", e.getRootCause() != null ? e.getRootCause().getMessage() : "Unknown root cause");
}
return new HttpResponse(httpStatus, null, body.toString());
}
}
snicoll
Metadata
Metadata
Assignees
Labels
in: webIssues in web modules (web, webmvc, webflux, websocket)Issues in web modules (web, webmvc, webflux, websocket)type: documentationA documentation taskA documentation task