web-dev-qa-db-fra.com

Netflix Feign - Propager le statut et l'exception par le biais de Microservices

J'utilise Netflix Feign pour appeler une opération d'un microservice A vers une autre opération d'un microservice B qui valide un code à l'aide de Spring Boot. 

Le fonctionnement de Microservice B lève une exception si la validation a été mauvaise. Ensuite, j'ai manipulé les microservices et renvoyé un HttpStatus.UNPROCESSABLE_ENTITY (422) comme suit:

@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;
    }

Ainsi, lorsque Microservice A appelle B dans une interface, le suivant:

@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());
}

et les validations échouent. Il renvoie une erreur interne 500 avec le message suivant:

{
  "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"
}

Mais je dois retourner le même que l'opération Microservice B.

Quels seraient les meilleurs moyens ou techniques pour propager Status and Exceptions par le biais de microservices utilisant Netflix Feign?

15
Pau

Vous pouvez utiliser une feinte ErrorDecoder 

https://github.com/OpenFeign/feign/wiki/Custom-error-handling

Voici un exemple

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);
    }

}

Pour que spring récupère le ErrorDecoder, vous devez le mettre sur le ApplicationContext:

@Bean
public MyErrorDecoder myErrorDecoder() {
  return new MyErrorDecoder();
}
17
Mathias Dpunkt

Prise sans vergogne pour une petite bibliothèque que j'ai créée qui utilise la réflexion pour redistribuer dynamiquement les exceptions vérifiées (et décochées si elles sont sur l'interface Feign) en fonction d'un code d'erreur renvoyé dans le corps de la réponse. 

Plus d'informations sur le fichier readme: https://github.com/coveo/feign-error-decoder

3
jebeaudet

Ecrivez votre mappeur d'exceptions personnalisé et enregistrez-le. Vous pouvez personnaliser les réponses.

Exemple complet est ici

public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

    @Override
    public Response toResponse(Throwable ex) {
        return Response.status(500).entity(YOUR_RETURN_OBJ_HERE).build();
    }

}
1
Tugrul

Ce que nous faisons est comme suit:

Partagez un fichier jar commun contenant des exceptions avec les deux microservices.

1.) Dans les microservices Une exception de conversion en classe DTO permet de dire ErreurInfo ..__ qui contiendra tous les attributs de votre exception personnalisée avec un type exceptionType, qui contiendra le nom de la classe d’exception.

2.) Lorsqu'il sera reçu sur le microservice B, il sera géré par ErrorDecoder dans le microservice B et essaiera de créer un objet exception à partir de exceptionType comme ci-dessous:

@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);
 }
0
asif malek

Depuis 2017, nous avons créé une bibliothèque qui le fait à partir d'annotations (ce qui le rend assez facile, comme pour les demandes/etc, pour le coder par annotations).

il vous permet fondamentalement de coder le traitement des erreurs comme suit:

@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);
}

Vous pouvez le trouver dans l’organisation OpenFeign: https://github.com/OpenFeign/feign-annotation-error-decoder

disclaimer: Je suis un contributeur à feindre et au dev principal pour ce décodeur d'erreur.

0
saintf