Java rest模板&;ResponseErrorHandler:处理给定不确定返回对象的错误的优雅方法
使用Java rest模板&;ResponseErrorHandler:处理给定不确定返回对象的错误的优雅方法,java,spring-boot,error-handling,resttemplate,Java,Spring Boot,Error Handling,Resttemplate,使用restemplate,我正在查询远程API以返回预期类型(如果是HTTP 2xx)或APIError(如果是HTTP 4xx/5xx)的对象 因为响应对象是不确定的,所以我实现了一个自定义的ResponseErrorHandler,并重写了handleError(ClientHttpResponse ClientHttpResponse),以便在发生错误时提取APIError。到目前为止一切顺利: @Component public class RemoteAPI { publi
restemplate
,我正在查询远程API以返回预期类型(如果是HTTP 2xx)或APIError(如果是HTTP 4xx/5xx)的对象
因为响应对象是不确定的,所以我实现了一个自定义的ResponseErrorHandler
,并重写了handleError(ClientHttpResponse ClientHttpResponse)
,以便在发生错误时提取APIError。到目前为止一切顺利:
@Component
public class RemoteAPI {
public UserOrders getUserOrders(User user) {
addAuthorizationHeader(httpHeaders, user.getAccessToken());
HttpEntity<TokenRequest> request = new HttpEntity<>(HEADERS);
return restTemplate.postForObject(CUSTOMER_ORDERS_URI, request, UserOrders.class);
}
private class APIResponseErrorHandler implements ResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) {
try {
APIError apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
} catch ...
}
}
private void refreshAccessToken(User user) {
addAuthorizationHeader(httpHeaders, user.getAccessSecret());
HttpEntity<TokenRequest> request = new HttpEntity<>(HEADERS);
user.setAccessToken(restTemplate.postForObject(TOKEN_REFRESH_URI, request, AccessToken.class));
}
}
优点:明确最初调用的方法
缺点:对瞬态值使用类变量。每个调用API的方法都有很多样板代码。错误处理逻辑分布在多个方法中
选项2:用户对象作为类变量/ResponseErrorHandler中的错误管理逻辑
优点:错误管理逻辑在一个地方
缺点:用户对象现在必须是一个类变量,并进行优雅的处理,因为用户对象不能在ResponseErrorHandler中访问,因此不能像以前那样将其传递给getUserOrders(User)
。需要跟踪每个方法被调用的次数
选项3:RemoteAPI类之外的错误管理逻辑
优点:将错误处理与业务逻辑分离
缺点:API逻辑现在在另一个类中
谢谢你的建议。回答我自己的问题:原来问题本身就有谬误 我实现了一个
ResponseErrorHandler
,因为我认为我需要它来解析响应,即使该响应返回了HTTP错误代码。事实上,情况并非如此
演示通过捕获HttpStatusCodeException
或使用标准的RestTemplate
,可以将响应解析为对象。这就不需要自定义ResponseErrorHandler
,因此不需要返回类型不明确的对象。传递错误的方法可以捕获HttpStatusCodeException
,尝试刷新访问令牌,然后通过递归再次调用自身。需要一个计数器来防止无休止的递归,但它可以被传递,而不是作为类变量
缺点是,它仍然需要在类中传播错误管理逻辑,以及大量的样板代码,但它比其他选项要整洁得多
public UserOrders getUserOrders(User user, Integer methodCallCount) {
methodCallCount++;
UserOrders userOrders;
try {
userOrders = restTemplate.postForObject(USER_ORDERS_URI, request, UserOrders.class);
} catch (RestClientException ex) {
APIError apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
if (methodCallCount < MAX_METHOD_CALLS) {
if (apiError.isType(ACCESS_TOKEN_EXPIRED)) {
refreshVendorAccessTokenInfo(user);
userOrders = getUserOrders(user, methodCallCount);
}
}
}
return userOrders;
}
公共用户订单getUserOrders(用户用户,整数方法调用计数){
methodCallCount++;
用户订单用户订单;
试一试{
userOrders=restemplate.postForObject(USER\u ORDERS\u URI、request、userOrders.class);
}捕获(RestClientException例外){
APIRROR APIRROR=newObjectMapper().readValue(response.getBody(),apierro.class);
if(methodCallCount@Component
public class RemoteAPI {
private User user;
private class APIResponseErrorHandler implements ResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) {
try {
APIError apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
// Check this.apiError for type of error
// Check how many times this API call has been attempted; compare against maximum
// Try again...
getUserOrders();
...or report back as a failure
} catch ...
}
}
public UserOrders getUserOrders(User user, Integer methodCallCount) {
methodCallCount++;
UserOrders userOrders;
try {
userOrders = restTemplate.postForObject(USER_ORDERS_URI, request, UserOrders.class);
} catch (RestClientException ex) {
APIError apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
if (methodCallCount < MAX_METHOD_CALLS) {
if (apiError.isType(ACCESS_TOKEN_EXPIRED)) {
refreshVendorAccessTokenInfo(user);
userOrders = getUserOrders(user, methodCallCount);
}
}
}
return userOrders;
}