使用してRestTemplate
、私は(HTTP 2xxの場合)のいずれか期待型のオブジェクトを返すために、リモートAPIを照会していますか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));
}
}
挑戦はそれであるgetUserOrders()
、または類似のAPIコールは、時折「回復可能」というエラーで失敗します。例えば、APIアクセストークンの有効期限が切れている可能性があります。私たちは、その後にAPI呼び出しを行う必要がありますrefreshAccessToken()
再試行する前にgetUserOrders()
。同じものは、それらが回復不能/重要とみなされるされる時点で複数回、発生したまではこのような回復可能なエラーがユーザーから隠されるべきです。
「重要」であり、任意のエラー(例:第二の失敗、完全な認証失敗、またはトランスポート層の障害)が利用可能な自動回復がないように、ユーザーに報告しなければなりません。
オブジェクトのタイプが実行時まで知られていない返されることに注意してロジック、ベアリングエラー処理を管理する最もエレガントで堅牢な方法は何ですか?
オプション1:各APIの呼び出し方法でのtry / catchでクラス変数としてエラーオブジェクト:
@Component
public class RemoteAPI {
private APIError apiError;
private class APIResponseErrorHandler implements ResponseErrorHandler {
@Override
public void handleError(ClientHttpResponse response) {
try {
this.apiError = new ObjectMapper().readValue(response.getBody(), APIError.class);
} catch ...
}
}
public UserOrders getUserOrders(User user) {
try {
userOrders = restTemplate.postForObject(CUSTOMER_ORDERS_URI, request, UserOrders.class);
} catch (RestClientException ex) {
// Check this.apiError for type of error
// Check how many times this API call has been attempted; compare against maximum
// Try again, or report back as a failure
}
return userOrders;
}
}
長所:クラリティ元々呼び出しを行っている方法に
短所:過渡値のクラス変数の使用。APIを呼び出す各メソッドの定型コードがたくさん。複数の方法の周りのロジックの広がりをエラー処理。
オプション2:ResponseErrorHandlerでクラス変数/エラー管理ロジックとしてUserオブジェクト
@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 ...
}
}
長所:エラー管理ロジックが一つの場所にあります。
短所:ユーザーオブジェクトがそう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;
}