2016-04-06 27 views
9

Ho uno scenario in Zuul in cui il servizio che l'URL viene instradato potrebbe essere inattivo. Quindi il corpo della risposta viene lanciato con 500 HTTP Status e ZuulException nella risposta del corpo JSON.Personalizzazione di Zuul Exception

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

Tutto quello che voglio fare è personalizzare o rimuovere la risposta JSON e magari cambiare il codice di stato HTTP.

Ho tentato di creare un gestore di eccezioni con @ControllerAdvice ma l'eccezione non è stata acquisita dal gestore.

AGGIORNAMENTI:

Così ho esteso il filtro Zuul posso vederlo entrare nel metodo run dopo l'errore è stato eseguito come faccio a cambiare la risposta allora. Di seguito è quello che ho ottenuto finora. Ho letto da qualche parte su SendErrorFilter ma come faccio a implementarlo e cosa fa?

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

Aggiunto questo per la classe che ha @EnableZuulProxy

@Bean 
public CustomFilter customFilter() { 
    return new CustomFilter(); 
} 
+0

Hai già provato qualcosa? –

+0

Ho provato ad aggiungere un gestore di eccezioni annotando la classe con @ControllerAdvice ma non sembra funzionare. Penso di aver bisogno di fare qualcosa con Zuul Filters ma non sono sicuro di cosa deve accadere. –

+1

OK, allora sarebbe bene che la tua domanda fosse modificata per fare questo tentativo, perché come puoi notare c'è qualche downvoter che pensava di non aver provato niente da solo. Se migliori la domanda ti darò il mio +1 perché lo considero un argomento interessante. –

risposta

14

Abbiamo finalmente ottenuto questo lavoro [codificata da un mio collega]: -

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; 
    } 
} 
+0

puoi consigliarti come puoi reindirizzare alla pagina di errore standard per qualsiasi eccezione verificatasi su zuul layer dato che non voglio hardcode ReponseBody. Grazie @ grinish-nepal – Joey

+0

@ Van ha risposto alla tua domanda reale. –

+0

quindi non hai aggiunto il filtro ** errore ** e solo il filtro post. –

4

Forwarding è spesso fatto da un filtro, in questo caso la richiesta non raggiunge neppure un controller. Questo spiegherebbe perché @ControllerAdvice non funziona.

Se si inoltra nel controller rispetto a @ControllerAdvice dovrebbe funzionare. Verifica se molla crea un'istanza della classe annotata con @ControllerAdvice. Per quel posto un punto di interruzione nella classe e vedere se viene colpito.

Aggiungere un punto di interruzione anche nel metodo del controller in cui deve avvenire l'inoltro. Potresti accidentalmente invocare un altro metodo di controllo di quello che controlli?

Questi passaggi dovrebbero aiutare a risolvere il problema.

Nella classe annotata con @ControllerAdvice, aggiungere un metodo ExceptionHandler annotato con @ExceptionHandler (Exception.class), che dovrebbe rilevare tutte le eccezioni.

MODIFICA: Si può provare ad aggiungere il proprio filtro che converte la risposta all'errore restituita dallo Zuulfilter. Lì puoi cambiare la risposta come preferisci.

Come la risposta di errore può essere personalizzato viene qui spiegato:

exception handling for filter in spring

Posizionando il filtro in modo corretto può essere un po 'difficile. Non esattamente sicuro della posizione corretta, ma è necessario conoscere l'ordine dei filtri e il punto in cui si gestisce l'eccezione.

Se lo si posiziona prima dello Zuulfilter, è necessario codificare la gestione degli errori dopo aver chiamato doFilter().

Se lo si posiziona dopo Zuulfilter, è necessario codificare la gestione degli errori prima di chiamare doFilter().

Aggiungere punti di interruzione nel filtro prima e dopo doFilter() può aiutare a trovare la posizione corretta.

+0

In realtà non sto inoltrando qualsiasi cosa qui Nella mia applicazione di avvio sporing tutto ciò che ho è @EnableZullProxy da spring-cloud in modo che possa aggiungere il mio routing alla configurazione. Quindi non c'è controller in là. Speravo che controllerAdvice potesse funzionare, ma dal momento che l'inoltro è fatto dal filtro ho bisogno di afferrare quella parte e personalizzarla che non riuscivo a capire come. –

+0

Grazie fammi provare questo ... ti aggiornerò .... –

3

Ho avuto lo stesso problema ed è stato in grado di risolvere in modo semplice

Basta mettere questo in si Filtra run() metodo

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

e SendErrorFilter consegnerà all'utente il messaggio con il statusCode desiderato.

Questa eccezione in un modello di eccezione non sembra esattamente bella, ma funziona qui.

+0

Quali sono i vantaggi dell'utilizzo di ZuulRuntimeException semplicemente utilizzando RuntimeException? – IcedDante

+0

In questo modo è possibile aggiungere un messaggio personalizzato e un codice di stato. Con 'RuntimeException' il codice di stato sarà 500. Non si è sicuri del messaggio –

2

Questi sono i passi da fare con @ControllerAdvice:

  1. Prima aggiungere un filtro di tipo error e lasciarlo essere eseguito prima che il SendErrorFilter in Zuul sé.
  2. Assicurarsi di rimuovere la chiave associata all'eccezione da RequestContext per impedire l'esecuzione di SendErrorFilter.
  3. Utilizzare RequestDispatcher per inoltrare la richiesta allo ErrorController - spiegato di seguito.
  4. Aggiungere una classe @RestController e farlo estendere AbstractErrorController e ri-lanciare nuovamente l'eccezione (aggiungerla nel passaggio dell'esecuzione del nuovo filtro di errore con (chiave, eccezione), scaricarla dallo RequestContext nel controller).

L'eccezione verrà ora rilevata nella classe @ControllerAdvice.

+0

questo funziona, eccetto che ho implementato 'ErrorController' nella classe' ControllerAdvice' e ho aggiunto l'annotazione 'RestController'. Questo forse non è bello, ma funziona. –