2012-06-28 9 views
23

Ich habe eine Spring MVC-Anwendung, die FreeMarker als View-Technologie verwendet (Aber vielleicht ist die View-Technologie für meine Frage nicht wirklich wichtig). Ich muss alle Ausnahmen abfangen, die während einer Anfrage ausgelöst werden können.Wie behandelt man Ausnahmen beim Rendern einer Ansicht in Spring MVC?

Ich habe eine HandlerExceptionResolver implementiert, aber dieser Resolver wird nur ausgeführt, wenn die Ausnahme innerhalb eines Controllers auftritt. Aber wenn ein Controller eine ModelAndView zurückgibt und die Ausnahme beim Rendern der Ansicht auftritt (Weil eine Variable nicht gefunden wurde oder so ähnlich, wird der Exception Resolver nicht aufgerufen und stattdessen bekomme ich einen Stack-Trace im Browserfenster.

Ich habe auch versucht, eine Exception-Handler-Methode innerhalb der Steuerung, die die Ansicht zurückgibt und es mit @ExceptionHandler kommentiert, aber dies funktioniert auch nicht (Am wahrscheinlichsten wieder, da die Ausnahme nicht in der Controller, sondern in der Ansicht ausgelöst wird) .

Gibt es also einen Spring-Mechanismus, wo ich einen Exception-Handler registrieren kann, der View-Fehler erfasst?

+0

Würde so [Konfiguration] (http://developingdeveloper.wordpress.com/2008/03/09/handling-exceptions-in-spring-mvc-part-2/) helfen? – nobeh

+0

@nobeh Nein, leider nicht. Dieser Artikel erklärt einfach die Verwendung des HandlerExceptionResolver-Zeugs. Das verwende ich bereits, aber es erfasst nur Ausnahmen, die in Controllern und nicht in Ansichten ausgelöst werden. – kayahr

Antwort

21

Ein Wort vorab: Wenn Sie nur eine "statische" Fehlerseite ohne viel Logik und Modellvorbereitung benötigen, sollte es ausreichen, einen <error-page> -Tag in Ihre web.xml zu setzen (siehe unten für ein Beispiel).

Andernfalls könnte es bessere Möglichkeiten, dies zu tun, aber das funktioniert für uns:

Wir verwenden ein Servlet <filter> im web.xml, die alle Ausnahmen abfängt und ruft unsere eigene Fehlerbehandler, die gleichen wir im Inneren des Frühlings verwenden HandlerExceptionResolver.

<filter> 
    <filter-name>errorHandlerFilter</filter-name> 
    <filter-class>org.example.filter.ErrorHandlerFilter</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>errorHandlerFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 

Die Umsetzung sieht im Wesentlichen wie folgt aus:

public class ErrorHandlerFilter implements Filter { 

    ErrorHandler errorHandler; 

    @Override 
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { 
    try { 
     filterChain.doFilter(request, response); 
    } catch (Exception ex) { 
     // call ErrorHandler and dispatch to error jsp 
     String errorMessage = errorHandler.handle(request, response, ex); 
     request.setAttribute("errorMessage", errorMessage); 
     request.getRequestDispatcher("/WEB-INF/jsp/error/dispatch-error.jsp").forward(request, response); 
    } 

    @Override 
    public void init(FilterConfig filterConfig) throws ServletException { 
    errorHandler = (ErrorHandler) WebApplicationContextUtils 
     .getRequiredWebApplicationContext(filterConfig.getServletContext()) 
     .getBean("defaultErrorHandler"); 
    } 

    // ... 
} 

Ich glaube, das sollte für Freemarker Vorlagen so ziemlich das gleiche Arbeit. Natürlich, wenn Ihre Fehleransicht einen Fehler verursacht, sind Sie mehr oder weniger aus Optionen.

Um auch Fehler wie 404 und bereiten das Modell für ihn zu fangen, verwenden wir einen Filter, der auf die ERROR Dispatcher zugeordnet ist:

<filter> 
    <filter-name>errorDispatcherFilter</filter-name> 
    <filter-class>org.example.filter.ErrorDispatcherFilter</filter-class> 
</filter> 
<filter-mapping> 
    <filter-name>errorDispatcherFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
    <dispatcher>ERROR</dispatcher> 
</filter-mapping> 

<error-page> 
    <error-code>404</error-code> 
    <location>/WEB-INF/jsp/error/dispatch-error.jsp</location> 
</error-page> 
<error-page> 
    <exception-type>java.lang.Exception</exception-type> 
    <location>/WEB-INF/jsp/error/dispatch-error.jsp</location> 
</error-page> 

Die doFilter-Implementierung sieht wie folgt aus:

@Override 
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 

    final HttpServletRequest request = (HttpServletRequest) servletRequest; 

    // handle code(s) 
    final int code = (Integer) request.getAttribute("javax.servlet.error.status_code"); 
    if (code == 404) { 
    final String uri = (String) request.getAttribute("javax.servlet.error.request_uri"); 
    request.setAttribute("errorMessage", "The requested page '" + uri + "' could not be found."); 
    } 

    // notify chain 
    filterChain.doFilter(servletRequest, servletResponse); 
} 
+0

Ich habe den benutzerdefinierten Fehlerhandler nicht verwendet. Alles, was ich tun möchte, ist - wenn die Vorlagenverarbeitung fehlschlägt, dann leite den Benutzer auf eine statische Fehlerseite um. Der Browser zeigt den Inhalt der Fehlerseite an, zeigt aber auch den Stack-Trace. Irgendwelche Hinweise? Ich benutze Frühling. Einfach nur Servlets mit Freemarker für Ansichten. – sbidwai

+1

Hallo, vielen Dank dafür, ich bekomme 'org.springframework.beans.factory.NoSuchBeanDefinitionException: Keine Bean namens 'defaultErrorHandler' ist auf der init definiert. Muss ich dies in einer servlet.xml definieren? – Ernest

+0

Gibt es eine Möglichkeit, dies vorwärts zu tun: 'request.getRequestDispatcher ("/WEB-INF/jsp/Fehler/dispatch-error.jsp "). Forward (Anfrage, Antwort);' mit einem vorhandenen ViewResolver? – FelipeKunzler

0

Nicht sicher, ob meine Lösung mit dem Problem, das Sie haben, funktioniert. Ill Post nur die Art, wie ich meine Ausnahmen fange kein Stack-Trace ist Show im Browser, um sicherzustellen:

ich eine AbstractController Klasse mit einem Verfahren hergestellt, das einen spezifischen Konflikt wie folgt behandelt:

public class AbstractController { 

    @ResponseStatus(HttpStatus.CONFLICT) 
    @ExceptionHandler({OptimisticLockingFailureException.class}) 
    @ResponseBody 
    public void handleConflict() { 
    //Do something extra if you want 
    } 
} 

Auf diese Weise Wenn eine Ausnahme auftritt, wird dem Benutzer der HTTPResponse-Standardstatus angezeigt. (z. B. 404 nicht gefunden usw.)

Ich erweitere diese Klasse auf alle meine Controller-Klassen, um sicherzustellen, dass Fehler an den AbstractController weitergeleitet werden. Auf diese Weise muss ich ExceptionHandler nicht für einen bestimmten Controller verwenden, sondern global für alle meine Controller. (durch Erweiterung der AbstractController-Klasse).

Bearbeiten: Nach einem anderen auf Ihre Frage gehen, bemerkte ich, dass Sie Fehler aus Ihrer Sicht bekommen. Nicht sicher, ob dieser Weg diesen Fehler fängt.

Hoffe, dass dies hilft !!

+0

Habe das schon versucht. Ich habe eine solche Methode direkt im Controller hinzugefügt und nicht in einer Basisklasse, aber das macht keinen Unterschied. Dieser Ausnahmebehandler wird aufgerufen, wenn die Ausnahme innerhalb der Controller-Methode ausgelöst wird, nicht jedoch, wenn die Ausnahme in der Ansicht ausgelöst wird. – kayahr

+0

oder wenn der Fehler in einem Servlet-Filter auftritt. Ich denke, dass die akzeptierte Antwort die einzige ist, die irgendwo einen Fehler behandelt. – ticktock

+0

Dies wird nicht funktionieren. Die Vorlagenverarbeitung erfolgt nach dem Aufruf von Controllern und Controller-Ratschlägen. Der einzige Weg, an den ich denken kann, ist das Hinzufügen einer Handlinie zum Filter, wie es die akzeptierte Antwort vorschlägt. Nicht schön, aber macht Sinn. –

6

Sie könnten das DispatcherServlet erweitern.

Ersetzen Sie in Ihrer web.xml das generische DispatcherServlet für Ihre eigene Klasse.

<servlet> 
    <servlet-name>springmvc</servlet-name> 
    <servlet-class>com.controller.generic.DispatcherServletHandler</servlet-class> 
    <load-on-startup>1</load-on-startup> 
</servlet> 

Später Ihre eigene Klasse DispatcherServletHandler erstellen und erstreckt sich von DispatcherServlet:

public class DispatcherServletHandler extends DispatcherServlet { 

    private static final String ERROR = "error"; 
    private static final String VIEW_ERROR_PAGE = "/WEB-INF/views/error/view-error.jsp"; 

    @Override 
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception { 
     try{ 
      super.doService(request, response); 
     } catch(Exception ex) { 
      request.setAttribute(ERROR, ex); 
      request.getRequestDispatcher(VIEW_ERROR_PAGE).forward(request, response); 
     } 
    } 
} 

Und in dieser Seite stellen wir nur eine Nachricht an den Benutzer zeigen müssen.

Verwandte Themen