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;
}