Zuul例外のカスタマイズ
1. 概要
Zuulは、NetflixによるJVMベースのルーターおよびサーバー側のロードバランサーです。 Zuulのルールエンジンは、Spring Cloudマイクロサービスアーキテクチャでルーティングを強化するためのルールとフィルターを作成する柔軟性を提供します。
この記事では、コード実行中にエラーが発生したときに実行されるカスタムエラーフィルターを記述して、Zuulで例外とエラー応答をカスタマイズする方法について説明します。
2. ズールの例外
Zuulで処理されるすべての例外は、ZuulExceptionsです。 ここで、 ZuulExceptionが@ControllerAdviceによってキャッチされないことを明確にし、@ExceptionHandlingによってメソッドにアノテーションを付けます。 これは、エラーフィルターから ZuulExceptionがスローされるためです。 したがって、後続のフィルターチェーンをスキップし、エラーコントローラーに到達することはありません。 次の図は、Zuulでのエラー処理の階層を示しています。
ZuulException がある場合、Zuulは次のエラー応答を表示します。
{
"timestamp": "2022-01-23T22:43:43.126+00:00",
"status": 500,
"error": "Internal Server Error"
}
シナリオによっては、ZuulExceptionの応答でエラーメッセージまたはステータスコードをカスタマイズする必要がある場合があります。Zuulフィルターが役に立ちます。 次のセクションでは、Zuulのエラーフィルターを拡張し、ZuulExceptionをカスタマイズする方法について説明します。
3. Zuul例外のカスタマイズ
spring-cloud-starter-netflix-zuul のスターターパックには、 pre 、 post 、エラーフィルターの3種類のフィルターが含まれています。 ここでは、エラーフィルターについて詳しく説明し、SendErrorFilterと呼ばれるZuulエラーフィルターのカスタマイズについて説明します。
まず、自動的に設定されるデフォルトのSendErrorFilterを無効にします。 これにより、これが唯一のZuulデフォルトエラーフィルターであるため、実行の順序について心配する必要がなくなります。 application.yml にプロパティを追加して、無効にします。
zuul:
SendErrorFilter:
post:
disable: true
次に、基になるサービスが利用できない場合にカスタム例外をスローするCustomZuulErrorFilterというカスタムZuulエラーフィルターを作成しましょう。
public class CustomZuulErrorFilter extends ZuulFilter {
}
このカスタムフィルターは、 com.netflix.zuul.ZuulFilter を拡張し、そのメソッドのいくつかをオーバーライドする必要があります。
まず、filterType()メソッドをオーバーライドし、タイプを「エラー」として返す必要があります。 これは、エラーフィルタータイプ用にZuulフィルターを構成するためです。
@Override
public String filterType() {
return "error";
}
その後、 filterOrder()をオーバーライドして-1を返し、フィルターがチェーンの最初のフィルターになるようにします:
@Override
public int filterOrder() {
return -1;
}
次に、 shouldFilter()メソッドをオーバーライドし、すべての場合にこのフィルターをチェーンしたいので、無条件にtrueを返します。
@Override
public boolean shouldFilter() {
return true;
}
最後に、 run()メソッドをオーバーライドしましょう。
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
Throwable throwable = context.getThrowable();
if (throwable instanceof ZuulException) {
ZuulException zuulException = (ZuulException) throwable;
if (throwable.getCause().getCause().getCause() instanceof ConnectException) {
context.remove("throwable");
context.setResponseBody(RESPONSE_BODY);
context.getResponse()
.setContentType("application/json");
context.setResponseStatusCode(503);
}
}
return null;
}
このrun()メソッドを分解して、何をしているのかを理解しましょう。 まず、RequestContextのインスタンスを取得します。 次に、RequestContextから取得したthrowableがZuulExceptionのインスタンスであるかどうかを確認します。 次に、throwableのネストされた例外の原因がConnectExceptionのインスタンスであるかどうかを確認します。 最後に、応答のカスタムプロパティを使用してコンテキストを設定しました。
カスタム応答を設定する前に、コンテキストからスロー可能オブジェクトをクリアして、フォローアップフィルターでのさらなるエラー処理を防止することに注意してください。
さらに、 run()メソッド内にカスタム例外を設定して、後続のフィルターで処理することもできます。
if (throwable.getCause().getCause().getCause() instanceof ConnectException) {
ZuulException customException = new ZuulException("", 503, "Service Unavailable");
context.setThrowable(customException);
}
上記のスニペットはスタックトレースをログに記録し、次のフィルターに進みます。
さらに、この例を変更して、ZuulFilter。内の複数の例外を処理できます。
4. カスタムZuul例外のテスト
このセクションでは、CustomZuulErrorFilterでカスタムZuul例外をテストします。
ConnectException があるとすると、ZuulAPIの応答での上記の例の出力は次のようになります。
{
"timestamp": "2022-01-23T23:10:25.584791Z",
"status": 503,
"error": "Service Unavailable"
}
さらに、 application.yml ファイルでerror.pathプロパティを構成することにより、デフォルトのZuulエラー転送パス/errorをいつでも変更できます。
それでは、いくつかのテストケースで検証してみましょう。
@Test
public void whenSendRequestWithCustomErrorFilter_thenCustomError() {
Response response = RestAssured.get("http://localhost:8080/foos/1");
assertEquals(503, response.getStatusCode());
}
上記のテストシナリオでは、 / foos / 1 のルートが意図的に停止されているため、java.lang。ConnectExceptionになります。 その結果、カスタムフィルタは、503ステータスでインターセプトして応答します。
次に、カスタムエラーフィルターを登録せずにこれをテストしましょう。
@Test
public void whenSendRequestWithoutCustomErrorFilter_thenError() {
Response response = RestAssured.get("http://localhost:8080/foos/1");
assertEquals(500, response.getStatusCode());
}
カスタムエラーフィルターを登録せずに上記のテストケースを実行すると、Zuulはステータス500で応答します。
5. 結論
このチュートリアルでは、エラー処理の階層について学び、SpringZuulアプリケーションでカスタムZuulエラーフィルターを構成する方法について詳しく説明しました。 このエラーフィルターは、応答の本文と応答コードをカスタマイズする機会を提供しました。 いつものように、サンプルコードはGitHubでから入手できます。