it-swarm.com.de

Netflix Feign - Status und Ausnahme über Microservices weitergeben

Ich verwende Netflix Feign , um eine Operation eines Microservice A zu einer anderen Operation eines Microservice B aufzurufen, die einen Code mithilfe von Spring Boot validiert. 

Der Betrieb von Microservice B löst eine Ausnahme aus, falls die Validierung schlecht war. Dann habe ich in den Microservices gehandhabt und einen HttpStatus.UNPROCESSABLE_ENTITY (422) wie folgt zurückgegeben:

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

Wenn also Microservice A als nächstes zu B in einer Schnittstelle aufruft:

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

die Validierungen schlagen fehl. Es wird ein interner Fehler 500 mit der nächsten Nachricht zurückgegeben:

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

Aber ich muss das gleiche wie die Microservice-Operation B zurückgeben.

Welches wären die besten Möglichkeiten oder Techniken, um Status und Ausnahmen über Microservices mit Netflix Feign zu verbreiten?

15
Pau

Sie könnten eine falsche ErrorDecoder verwenden 

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

Hier ist ein Beispiel

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

}

Damit der ErrorDecoder im Frühling abgeholt werden kann, muss er in den ApplicationContext eingefügt werden:

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

Schamloser Plug für eine kleine Bibliothek Ich habe Reflection verwendet, um überprüfte Ausnahmen (und ungeprüft, wenn sie sich auf der Feign-Schnittstelle befinden) dynamisch erneut auszulösen, basierend auf einem im Hauptteil der Antwort zurückgegebenen Fehlercode. 

Weitere Informationen zur Readme-Datei: https://github.com/coveo/feign-error-decoder

3
jebeaudet

OpenFeigns FeignException bindet nicht an einen bestimmten HTTP-Status (d. H. Verwendet nicht Spring's @ResponseStatus Annotation), wodurch Spring standardmäßig 500 wann immer mit einem FeignException konfrontiert. Das ist in Ordnung, da ein FeignException zahlreiche Ursachen haben kann, die nicht mit einem bestimmten HTTP-Status zusammenhängen.

Sie können jedoch die Art und Weise ändern, wie Spring FeignExceptions behandelt. Definieren Sie einfach ein ExceptionHandler, das das FeignException so behandelt, wie Sie es benötigen (siehe hier ):

@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(FeignException.class)
    public String handleFeignStatusException(FeignException e, HttpServletResponse response) {
        response.setStatus(e.status());
        return "feignError";
    }

}

In diesem Beispiel gibt Spring denselben HTTP-Status zurück, den Sie von Microservice B erhalten haben. Sie können auch den ursprünglichen Antworttext zurückgeben:

response.getOutputStream().write(e.content());
1
moe1

Schreiben Sie Ihren benutzerdefinierten Exception-Mapper und registrieren Sie ihn. Sie können die Antworten anpassen.

Vollständiges Beispiel ist hier

public class GenericExceptionMapper implements ExceptionMapper<Throwable> {

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

}
1
Tugrul

Was wir machen, ist wie folgt:

Teilen Sie ein gemeinsames Glas, das Ausnahmen enthält, mit beiden Microservices.

1.) In microservices Eine Konvertierungsausnahme in eine DTO-Klasse lautet beispielsweise ErrorInfo . Diese enthält alle Attribute Ihrer benutzerdefinierten Ausnahme mit einem String exceptionType, der den Namen der Ausnahmeklasse enthält.

2.) Wenn es bei microservice B empfangen wird, wird es von ErrorDecoder in microservice B verarbeitet, und es wird versucht, ein Ausnahmeobjekt aus exceptionType wie folgt zu erstellen:

@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

Seit 2017 haben wir eine Bibliothek erstellt, die dies aus Annotationen macht (was es ziemlich einfach macht, genau wie bei request/etc, durch Annotationen zu codieren).

im Grunde können Sie die Fehlerbehandlung folgendermaßen codieren:

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

Sie finden es in der OpenFeign-Organisation: https://github.com/OpenFeign/feign-annotation-error-decoder

haftungsausschluss: Ich bin ein Mitarbeiter von FEIGN und der Hauptentwickler für diesen Fehlerdecoder.

0
saintf