web-dev-qa-db-fra.com

Personnalisation de l'exception Zuul

J'ai un scénario dans Zuul où le service que l'URL est également routé pourrait être en panne. Ainsi, le corps de réponse reçoit 500 statuts HTTP et une exception ZuulException dans la réponse du corps JSON.

{
  "timestamp": 1459973637928,
  "status": 500,
  "error": "Internal Server Error",
  "exception": "com.netflix.zuul.exception.ZuulException",
  "message": "Forwarding error"
}

Tout ce que je veux, c'est personnaliser ou supprimer la réponse JSON et peut-être changer le code d'état HTTP.

J'ai essayé de créer un gestionnaire d'exception avec @ControllerAdvice mais l'exception n'a pas été saisie par le gestionnaire.

MISES À JOUR:

J'ai donc étendu le filtre Zuul. Je peux le voir entrer dans la méthode d'exécution après que l'erreur a été exécutée. Comment puis-je modifier la réponse ensuite. Ci-dessous est ce que j'ai eu jusqu'à présent. J'ai lu quelque part à propos de SendErrorFilter mais comment puis-je implémenter cela et que fait-il?

public class CustomFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return "post";
    }

    @Override
    public int filterOrder() {

        return 1;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        final RequestContext ctx = RequestContext.getCurrentContext();
        final HttpServletResponse response = ctx.getResponse();
        if (HttpStatus.INTERNAL_SERVER_ERROR.value() == ctx.getResponse().getStatus()) {
            try {
                response.sendError(404, "Error Error"); //trying to change the response will need to throw a JSON body.
            } catch (final IOException e) {
                e.printStackTrace();
            } ;
        }

        return null;
    }

Ajouté ceci à la classe qui a @EnableZuulProxy

@Bean
public CustomFilter customFilter() {
    return new CustomFilter();
}
14
Grinish Nepal

Nous avons finalement réussi à faire fonctionner ce travail [codé par un de mes collègues]: -

public class CustomErrorFilter extends ZuulFilter {

    private static final Logger LOG = LoggerFactory.getLogger(CustomErrorFilter.class);
    @Override
    public String filterType() {
        return "post";
    }

    @Override
    public int filterOrder() {
        return -1; // Needs to run before SendErrorFilter which has filterOrder == 0
    }

    @Override
    public boolean shouldFilter() {
        // only forward to errorPath if it hasn't been forwarded to already
        return RequestContext.getCurrentContext().containsKey("error.status_code");
    }

    @Override
    public Object run() {
        try {
            RequestContext ctx = RequestContext.getCurrentContext();
            Object e = ctx.get("error.exception");

            if (e != null && e instanceof ZuulException) {
                ZuulException zuulException = (ZuulException)e;
                LOG.error("Zuul failure detected: " + zuulException.getMessage(), zuulException);

                // Remove error code to prevent further error handling in follow up filters
                ctx.remove("error.status_code");

                // Populate context with new response values
                ctx.setResponseBody(“Overriding Zuul Exception Body”);
                ctx.getResponse().setContentType("application/json");
                ctx.setResponseStatusCode(500); //Can set any error code as excepted
            }
        }
        catch (Exception ex) {
            LOG.error("Exception filtering in custom error filter", ex);
            ReflectionUtils.rethrowRuntimeException(ex);
        }
        return null;
    }
}
21
Grinish Nepal

J'ai eu le même problème et j'ai pu le résoudre plus simplement

Il suffit de mettre cela dans votre méthode Filter run()

    if (<your condition>) {
        ZuulException zuulException = new ZuulException("User message", statusCode, "Error Details message");
        throw new ZuulRuntimeException(zuulException);
    }

et SendErrorFilter remettra à l'utilisateur le message avec la statusCode souhaitée.

Cette exception dans un modèle d’exception ne s’apparente pas exactement à Nice, mais elle fonctionne ici.

5
Vasile Rotaru

Le transfert est souvent effectué par un filtre; dans ce cas, la demande n'atteint même pas le contrôleur. Cela expliquerait pourquoi votre @ControllerAdvice ne fonctionne pas.

Si vous transmettez dans le contrôleur, le @ControllerAdvice devrait fonctionner . Vérifiez si spring crée une instance de la classe annotée avec @ControllerAdvice. Pour cela, placez un point d'arrêt dans la classe et voyez s'il est touché.

Ajoutez également un point d'arrêt dans la méthode du contrôleur où le transfert devrait avoir lieu. Peut-être que vous invoquez accidentellement une autre méthode de contrôleur que vous inspectez?

Ces étapes devraient vous aider à résoudre le problème.

Dans votre classe annotée avec @ControllerAdvice, ajoutez une méthode ExceptionHandler annotée avec @ExceptionHandler (Exception.class), qui devrait intercepter toutes les exceptions. 

EDIT: Vous pouvez essayer d'ajouter votre propre filtre qui convertit la réponse d'erreur renvoyée par le Zuulfilter. Là, vous pouvez changer la réponse à votre guise.

La manière dont la réponse d'erreur peut être personnalisée est expliquée ici:

traitement des exceptions pour le filtre au printemps

Placer le filtre correctement peut être un peu délicat. Vous ne savez pas exactement quelle est la position correcte, mais vous devez connaître l'ordre de vos filtres et l'emplacement où vous gérez l'exception.

Si vous le placez avant le Zuulfilter, vous devez coder le traitement de votre erreur après avoir appelé doFilter ().

Si vous le placez après le Zuulfilter, vous devez coder le traitement de votre erreur avant d'appeler doFilter ().

Ajouter des points d'arrêt dans votre filtre avant et après doFilter () peut aider à trouver la position correcte.

Voici les étapes à suivre avec @ControllerAdvice:

  1. Ajoutez d’abord un filtre de type error et laissez-le s’exécuter avant la SendErrorFilter dans zuul même.
  2. Assurez-vous de supprimer la clé associée à l'exception de RequestContext pour empêcher l'exécution de SendErrorFilter.
  3. Utilisez RequestDispatcher pour transmettre la demande à la ErrorController - expliquée ci-dessous.
  4. Ajoutez une classe @RestController et agrandissez-la AbstractErrorController, puis relancez l'exception (ajoutez-la à l'étape de l'exécution de votre nouveau filtre d'erreur avec (clé, exception), récupérez-la à partir de la RequestContext de votre contrôleur).

L'exception sera maintenant interceptée dans votre classe @ControllerAdvice.

3
Karim Masoud

Zuul RequestContext ne contient pas le error.exception mentionné dans cette réponse .
Mettre à jour le filtre d’erreur Zuul:

@Component
public class ErrorFilter extends ZuulFilter {
    private static final Logger LOG = LoggerFactory.getLogger(ErrorFilter.class);

    private static final String FILTER_TYPE = "error";
    private static final String THROWABLE_KEY = "throwable";
    private static final int FILTER_ORDER = -1;

    @Override
    public String filterType() {
        return FILTER_TYPE;
    }

    @Override
    public int filterOrder() {
        return FILTER_ORDER;
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() {
        final RequestContext context = RequestContext.getCurrentContext();
        final Object throwable = context.get(THROWABLE_KEY);

        if (throwable instanceof ZuulException) {
            final ZuulException zuulException = (ZuulException) throwable;
            LOG.error("Zuul failure detected: " + zuulException.getMessage());

            // remove error code to prevent further error handling in follow up filters
            context.remove(THROWABLE_KEY);

            // populate context with new response values
            context.setResponseBody("Overriding Zuul Exception Body");
            context.getResponse().setContentType("application/json");
            // can set any error code as excepted
            context.setResponseStatusCode(503);
        }
        return null;
    }
}
0
xxxception