Netflix Feign を使用して、Spring Bootを使用してコードを検証するMicroservice Aの1つの操作をMicroservice Bの他の操作に呼び出しています。
検証に問題がある場合、Microservice Bの操作は例外をスローします。次に、マイクロサービスで処理し、HttpStatus.UNPROCESSABLE_ENTITY
(422)次のように:
@ExceptionHandler({
ValidateException.class
})
@ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
@ResponseBody
public Object validationException(final HttpServletRequest request, final validateException exception) {
log.error(exception.getMessage(), exception);
error.setErrorMessage(exception.getMessage());
error.setErrorCode(exception.getCode().toString());
return error;
}
したがって、マイクロサービスAが次のようにインターフェースでBを呼び出す場合:
@Headers("Content-Type: " + MediaType.APPLICATION_JSON_UTF8_VALUE)
@RequestLine("GET /other")
void otherOperation(@Param("other") String other );
@Headers("Content-Type: " + MediaType.APPLICATION_JSON_UTF8_VALUE)
@RequestLine("GET /code/validate")
Boolean validate(@Param("prefix") String prefix);
static PromotionClient connect() {
return Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(PromotionClient.class, Urls.SERVICE_URL.toString());
}
そして検証が失敗します次のメッセージで内部エラー500を返します:
{
"timestamp": "2016-08-05T09:17:49.939+0000",
"status": 500,
"error": "Internal Server Error",
"exception": "feign.FeignException",
"message": "status 422 reading Client#validate(String); content:\n{\r\n \"errorCode\" : \"VALIDATION_EXISTS\",\r\n \"errorMessage\" : \"Code already exists.\"\r\n}",
"path": "/code/validate"
}
ただし、Microservice操作Bと同じものを返す必要があります。
Wifは、Netflix Feignを使用してマイクロサービスを通じてステータスと例外を伝播する最良の方法または手法でしょうか?
偽のErrorDecoder
を使用できます
https://github.com/OpenFeign/feign/wiki/Custom-error-handling
ここに例があります
public class MyErrorDecoder implements ErrorDecoder {
private final ErrorDecoder defaultErrorDecoder = new Default();
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
return new MyBadRequestException();
}
return defaultErrorDecoder.decode(methodKey, response);
}
}
SpringがErrorDecoderを取得するには、それをApplicationContextに配置する必要があります。
@Bean
public MyErrorDecoder myErrorDecoder() {
return new MyErrorDecoder();
}
リフレクションを使用して、応答の本文に返されたエラーコードに基づいてチェック例外を動的に再スローする(およびFeignインターフェイスにある場合はチェックを外す)小さなライブラリ用の恥知らずなプラグイン。
READMEの詳細: https://github.com/coveo/feign-error-decoder
OpenFeignの FeignException は特定のHTTPステータスにバインドしません(つまり、Springの@ResponseStatus
アノテーション)、Springのデフォルトを500
FeignException
に直面したとき。 FeignException
には、特定のHTTPステータスに関連しない多くの原因があるため、これは問題ありません。
ただし、SpringがFeignExceptions
を処理する方法を変更できます。 ExceptionHandler
を必要な方法で処理するFeignException
を定義するだけです( here を参照)。
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(FeignException.class)
public String handleFeignStatusException(FeignException e, HttpServletResponse response) {
response.setStatus(e.status());
return "feignError";
}
}
この例では、SpringがMicroservice Bから受け取ったものと同じHTTPステータスを返します。さらに進んで、元の応答本文を返すこともできます。
response.getOutputStream().write(e.content());
カスタム例外マッパーを作成して登録します。応答をカスタマイズできます。
完全な例は here です
public class GenericExceptionMapper implements ExceptionMapper<Throwable> {
@Override
public Response toResponse(Throwable ex) {
return Response.status(500).entity(YOUR_RETURN_OBJ_HERE).build();
}
}
私たちが行うことは次のとおりです。
例外を含む共通のjarを両方のマイクロサービスと共有します。
1.)マイクロサービスでDTOクラスへの変換例外は、ErrorInfoと言います。これには、例外クラス名を含むString exceptionTypeを持つカスタム例外のすべての属性が含まれます。
2.)マイクロサービスBで受信されると、マイクロサービスBのErrorDecoderによって処理され、次のようにexceptionTypeから例外オブジェクトを作成しようとします。
@Override
public Exception decode(String methodKey, Response response) {
ErrorInfo errorInfo = objectMapper.readValue(details, ErrorInfo.class);
Class exceptionClass;
Exception decodedException;
try {
exceptionClass = Class.forName(errorInfo.getExceptionType());
decodedException = (Exception) exceptionClass.newInstance();
return decodedException;
}
catch (ClassNotFoundException e) {
return new PlatformExecutionException(details, errorInfo);
}
return defaultErrorDecoder.decode(methodKey, response);
}
2017年以降、注釈からこれを行うライブラリを作成しました(リクエストなどと同様に、注釈によってこれを簡単にコーディングできるようにします)。
基本的に、次のようにエラー処理をコーディングできます。
@ErrorHandling(codeSpecific =
{
@ErrorCodes( codes = {401}, generate = UnAuthorizedException.class),
@ErrorCodes( codes = {403}, generate = ForbiddenException.class),
@ErrorCodes( codes = {404}, generate = UnknownItemException.class),
},
defaultException = ClassLevelDefaultException.class
)
interface GitHub {
@ErrorHandling(codeSpecific =
{
@ErrorCodes( codes = {404}, generate = NonExistentRepoException.class),
@ErrorCodes( codes = {502, 503, 504}, generate = RetryAfterCertainTimeException.class),
},
defaultException = FailedToGetContributorsException.class
)
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
}
OpenFeign組織で見つけることができます: https://github.com/OpenFeign/feign-annotation-error-decoder
免責事項:私は、fignの寄稿者であり、そのエラーデコーダーのメイン開発者です。