1. 概要

この短いチュートリアルでは、ResponseErrorHandlerインターフェースをRestTemplateインスタンスに実装して挿入し、リモートAPIによって返されるHTTPエラーを適切に処理する方法について説明します。

2. デフォルトのエラー処理

デフォルトでは、HTTPエラーが発生した場合、RestTemplateは次のいずれかの例外をスローします。

  1. HttpClientErrorException –HTTPステータス4xxの場合
  2. HttpServerErrorException –HTTPステータス5xxの場合
  3. UnknownHttpStatusCodeException –不明なHTTPステータスの場合

これらの例外はすべて、RestClientResponseExceptionの拡張です。

明らかに、カスタムエラー処理を追加する最も簡単な戦略は、呼び出しをtry / catchブロックでラップすることです。次に、キャッチされた例外を適切と思われる方法で処理します。

ただし、この単純な戦略は、リモートAPIまたは呼び出しの数が増えるにつれてうまく拡張できません。 すべてのリモート呼び出しに再利用可能なエラーハンドラーを実装できれば、より効率的です。

3. ResponseErrorHandlerの実装

したがって、 ResponseErrorHandler を実装するクラスは、応答からHTTPステータスを読み取ります。

  1. アプリケーションにとって意味のある例外をスローします
  2. HTTPステータスを無視して、応答フローを中断せずに続行させます

ResponseErrorHandler実装をRestTemplateインスタンスに挿入する必要があります。

したがって、 RestTemplateBuilder を使用してテンプレートを作成し、応答フローのDefaultResponseErrorHandlerを置き換えます。

それでは、最初に RestTemplateResponseErrorHandler:を実装しましょう。

@Component
public class RestTemplateResponseErrorHandler 
  implements ResponseErrorHandler {

    @Override
    public boolean hasError(ClientHttpResponse httpResponse) 
      throws IOException {

        return (
          httpResponse.getStatusCode().series() == CLIENT_ERROR 
          || httpResponse.getStatusCode().series() == SERVER_ERROR);
    }

    @Override
    public void handleError(ClientHttpResponse httpResponse) 
      throws IOException {

        if (httpResponse.getStatusCode()
          .series() == HttpStatus.Series.SERVER_ERROR) {
            // handle SERVER_ERROR
        } else if (httpResponse.getStatusCode()
          .series() == HttpStatus.Series.CLIENT_ERROR) {
            // handle CLIENT_ERROR
            if (httpResponse.getStatusCode() == HttpStatus.NOT_FOUND) {
                throw new NotFoundException();
            }
        }
    }
}

次に、RestTemplateBuilderを使用してRestTemplate インスタンスを構築し、 RestTemplateResponseErrorHandler を導入します。

@Service
public class BarConsumerService {

    private RestTemplate restTemplate;

    @Autowired
    public BarConsumerService(RestTemplateBuilder restTemplateBuilder) {
        RestTemplate restTemplate = restTemplateBuilder
          .errorHandler(new RestTemplateResponseErrorHandler())
          .build();
    }

    public Bar fetchBarById(String barId) {
        return restTemplate.getForObject("/bars/4242", Bar.class);
    }

}

4. 実装のテスト

最後に、サーバーをモックして NOT_FOUND ステータスを返すことにより、このハンドラーをテストしてみましょう。

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = { NotFoundException.class, Bar.class })
@RestClientTest
public class RestTemplateResponseErrorHandlerIntegrationTest {

    @Autowired 
    private MockRestServiceServer server;
 
    @Autowired 
    private RestTemplateBuilder builder;

    @Test
    public void  givenRemoteApiCall_when404Error_thenThrowNotFound() {
        Assertions.assertNotNull(this.builder);
        Assertions.assertNotNull(this.server);

        RestTemplate restTemplate = this.builder
          .errorHandler(new RestTemplateResponseErrorHandler())
          .build();

        this.server
          .expect(ExpectedCount.once(), requestTo("/bars/4242"))
          .andExpect(method(HttpMethod.GET))
          .andRespond(withStatus(HttpStatus.NOT_FOUND));

        Assertions.assertThrows(NotFoundException.class, () -> {
            Bar response = restTemplate.getForObject("/bars/4242", Bar.class);
        });
    }
}

5. 結論

この記事では、HTTPエラーを意味のある例外に変換するRestTemplateのカスタムエラーハンドラーを実装およびテストするためのソリューションを紹介しました。

いつものように、この記事で紹介するコードは、Githubから入手できます。