Verkatert – Fehlerseiten in Spring Cloud Zuul

Nachdem ich einige Tage/Wochen mit Spring Cloud und Zuul arbeite, kam heute ein interessanter Anwendungsfall auf meinen Tisch. Wir benutzen den Zuulproxy nicht nur zum Weiterleiten von REST-Requests sind auch um Web-UI-Services aufzurufen, die HTML rendern. Was passiert wenn dieser Service nicht erreichbar ist?

Jeder, der mit Zuul gearbeitet hat wird den folgenden Screenshot kennen

Zuul Forwarding Error

Im September letzten Jahres hat Jakub Narloch in seinem Blog eine Lösung vorgestellt. Diese Lösung funktioniert aber nur solange die Annotation @ResponseBody angegeben ist. In unserem Fall wollen wir jedoch eine vollständige Fehlerseite rendern. Wie schon in seinem Artikel beschrieben ist der “Übeltäter” der SendErrorFilter. Dieser fängt den Fehler ab und ruft die Error-URL auf. Allerdings macht er noch etwas anderes. Er setzt das Attribut javax.servlet.error.exception in den Request.

if (ctx.containsKey("error.exception")) {
 Object e = ctx.get("error.exception");
 log.warn("Error during filtering", Throwable.class.cast(e));
 ctx.getRequest().setAttribute("javax.servlet.error.exception", e);
}

Dies führt dazu, dass der nicht Spring die Fehlerseite rendert sondern der Tomcat selber. Es gibt jetzt zwei Möglichkeiten das Problem zu lösen.

Lösung 1

Variante 1 sieht so aus, dass der SendErrorFilter über die Spring-Konfiguration (z.B. application.yml) abgeschaltet wird

zuul:
  SendErrorFilter.post.disable: true

und statt dessen ein eigener Filter erstellt wird, der die obige Zeile ctx.getRequest().setAttribute("javax.servlet.error.exception",e); nicht enthält. Damit funkioniert der normale ErrorController von Spring.

Lösung 2

Alternativ entfernt man das Attribut einfach im Controller.

@Controller
public class WebProxyErrorController implements ErrorController {

    @Value("${error.path:/error}")
    private String errorPath;

    @Override
    public String getErrorPath() {
        return errorPath;
    }

    @RequestMapping(value = "${error.path:/error}")
    public String error(HttpServletRequest request) {
        request.removeAttribute("javax.servlet.error.exception");
        return "error";
    }
}

Fazit

Ich habe mich für Variante 2 entschieden, da es aus meiner Sicht die elegantere Version ist. Eventuell gibt es auch die Möglichkeit den Tomcat von Spring Boot entsprechend zu konfigurieren, dass er das Attribut ignoriert, aber das vielleicht ein anderes Mal.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.