OpenFeign-エラーコード分岐処理(ErrorDecoder)
22743 ワード
前回の記事に続き,今回は通常の応答ではなく復号エラー応答のErrorDecoderを実現することを試みる.
まずFeignで応答エラーを処理する方法について理解する.
ErrorDecoder
ステータスコード400番→FeignClientException ステータスコード500番→FeignServerException
FeignExceptionを継承するFeignClientExceptionは、ステータスコードごとに異なるサブクラスを持つ.
400ならFeignExceptionBadRequest()、401はFeignExceptionです.Unautorized()認識で、次のコードでどのような異常があるかを確認できます.
500回のレスポンスを処理するFeignServerExceptionのタイプは多くありません.エラーコード処理
応答は、特定のフォーマットで提供される: エラーコードが存在する場合、 どちらも満足していれば
上記の要件をテストで表現します.
以下のBerClientErrorDecoderによりErrorDecoderを実現する.
上記の例は、次のソースコードで詳細に表示できます.
https://github.com/hanqyu/feign-decoder-example
まずFeignで応答エラーを処理する方法について理解する.
ErrorDecoder
Springcloud openfeignは、ErrorDecoderを介して応答値が200でない場合にエラーを投げ出す.
Feignのdefault ErrorDecoderはResponseEntityErrorDecoder
であり、対応するErrorDecoderはFeignException
である.
FeignClientExceptionとFeignServer Exception
FeignClientException
FeignExceptionを継承するFeignClientExceptionは、ステータスコードごとに異なるサブクラスを持つ.
400ならFeignExceptionBadRequest()、401はFeignExceptionです.Unautorized()認識で、次のコードでどのような異常があるかを確認できます.
// feign.FeignException#clientErrorStatus
switch (status) {
case 400:
return new BadRequest(message, request, body);
case 401:
return new Unauthorized(message, request, body);
case 403:
return new Forbidden(message, request, body);
case 404:
return new NotFound(message, request, body);
case 405:
return new MethodNotAllowed(message, request, body);
case 406:
return new NotAcceptable(message, request, body);
case 409:
return new Conflict(message, request, body);
case 410:
return new Gone(message, request, body);
case 415:
return new UnsupportedMediaType(message, request, body);
case 429:
return new TooManyRequests(message, request, body);
case 422:
return new UnprocessableEntity(message, request, body);
default:
return new FeignClientException(status, message, request, body);
}
FeignServerException
500回のレスポンスを処理するFeignServerExceptionのタイプは多くありません.
// feign.FeignException#serverErrorStatus
switch (status) {
case 500:
return new InternalServerError(message, request, body);
case 501:
return new NotImplemented(message, request, body);
case 502:
return new BadGateway(message, request, body);
case 503:
return new ServiceUnavailable(message, request, body);
case 504:
return new GatewayTimeout(message, request, body);
default:
return new FeignServerException(status, message, request, body);
}
エラーコード処理
外部サービスが特定のエラーコードを有する場合に応答する場合、異なる動作が必要になる場合があります.この場合,エラー応答を割り当てるbodyが必要であり,FallbackFactoryではその操作を実行するたびに面倒なことになる.
このとき、ErrorDecoderは、基本的なエラー応答パケットを処理することができる.
間違った方向をきれいにしてもらえませんか?
以下の応答がある場合は、エラーを吐かずに空のlistで応答することを考慮してください.
前シリーズのBeerClientに基づきます.status code 400
{
"result": "FAILED",
"data": null,
"error": {
"code": "FOO_ERROR",
"message": "error message",
"data": null
}
}
この場合、元のFeignExceptionであれば.BadRequestは投げ出されますが、FeignExceptionの応答bodyはbytearrayなので、応答を分解するたびに面倒です.
以下の方法で改善します.
status code 400
{
"result": "FAILED",
"data": null,
"error": {
"code": "FOO_ERROR",
"message": "error message",
"data": null
}
}
BeerClientHandledException
を投げます.テスト
上記の要件をテストで表現します.
@Test
fun handledError() {
val responseBody = """
{
"result": "FAILED",
"data": null,
"error": {
"code": "FOO_ERROR",
"message": "error message",
"data": null
}
}
""".trimIndent()
mockServer.stubFor(
get(urlPathEqualTo("/beers"))
.withQueryParam("size", equalTo("2"))
.withHeader("Authorization", equalTo("AUTH-KEY"))
.withHeader("Content-Type", equalTo("application/json"))
.willReturn(
aResponse()
.withStatus(HttpStatus.BAD_REQUEST.value())
.withBody(responseBody)
.withHeader("Content-Type", "application/json")
)
)
val then = shouldThrow<BeerClientHandledException> { sut.getBeer(2) }
then.errorCode shouldBe "FOO_ERROR"
then.message shouldBe "error message"
}
実行してみると、結果はもちろん失敗した.ErrorDecoder実装
以下のBerClientErrorDecoderによりErrorDecoderを実現する.
class BeerClientErrorDecoder: ErrorDecoder {
override fun decode(methodKey: String, response: Response): Exception {
val errorData = parse(response)
errorData?.code?.let {
return BeerClientHandledException(it, errorData.message)
}
return ErrorDecoder.Default().decode(methodKey, response)
}
private fun parse(response: Response): CommonResponse.ErrorData? {
return runCatching {
objectMapper.readValue(response.body().asInputStream(), CommonResponse::class.java)?.error
}.getOrNull()
}
private val objectMapper = jacksonObjectMapper()
.findAndRegisterModules()
}
data class CommonResponse<T>(
val result: String?,
val data: T?,
val error: ErrorData?
) {
data class ErrorData(
val code: String?,
val message: String?,
val data: Map<String, Any?>?
)
}
その後、クライアントは、対応するErrorDecoderを受信するように設定される.class BeerClientConfiguration {
@Bean
fun errorDecoder(): ErrorDecoder {
return BeerClientErrorDecoder()
}
}
もう一度テストを実行すると、テストが合格したことがわかります.ソース
上記の例は、次のソースコードで詳細に表示できます.
https://github.com/hanqyu/feign-decoder-example
Reference
この問題について(OpenFeign-エラーコード分岐処理(ErrorDecoder)), 我々は、より多くの情報をここで見つけました https://velog.io/@hanqyu/Spring-OpenFeign-에러에-더-많은-정보-담기-ErrorDecoderテキストは自由に共有またはコピーできます。ただし、このドキュメントのURLは参考URLとして残しておいてください。
Collection and Share based on the CC Protocol