wtorek, 20 marca 2012

Globalne łapanie wyjątków w Spring 3.1

Wielokrotnie wchodząc na przeróżne strony zamiast oczekiwanej przeze mnie treści pojawiała się strona z błędami aplikacji, i nie był to tylko komunikat w stylu "Błąd strony" a pełne kody błędów, łącznie ze stacktracem czy też kodem strony (jeśli była pisana w języku skryptowym). Nie trzeba chyba nikomu tłumaczyć, że tego typu treść nigdy nie powinna być widoczna dla użytkownika końcowego. I nie tylko dlatego, że strona taka brzydko wygląda i może odstraszyć użytkowników. Treści wyjątków jakie powoduje aplikacja, czy też część kodu jakie pojawią się mogą być bez problemu wykorzystane przez osoby interesujące się zabezpieczeniami.


W bibliotece SpringFramework, istnieje coś takiego jak ExceptionHandler, który działa lokalnie i jest implementowany per kontroler co przy większej aplikacji może być mało wygodne. Niestety globalny mechanizm przechwytywania powstanie najwcześniej w wersji 3.2(kiedyś przeczytałem pewien komentarz programisty Springa w ich systemie Jira) więc puki co musimy radzić sobie sami. Prosty system przechwytujący wyjątki można napisać samemu i nawet specjalnie się przy tym nie napracujemy.
Do stworzenia takiego systemu będziemy potrzebować najnowszych bibliotek Springa oraz bibliotek wspomagających programowanie aspektowe (aspectjweaver)

Na początek skonfigurujemy klasę która będzie obsługiwać wyjątki, konfigurację umieszczamy w applicationContext.xml

<aop:aspectj-autoproxy />
<aop:config>
<aop:aspect id="exceptionHandler" ref="globalExceptionHandler">
<!-- find all methods that returns ModelAndView objects -->
<aop:pointcut expression="execution(org.springframework.web.servlet.ModelAndView+ *(..))" id="returningMav" />
<!-- and execute them inside exceptionHandler -->
<aop:around method="exceptionHandler" pointcut-ref="returningMav" />
</aop:aspect>
</aop:config>
<bean id="globalExceptionHandler" class="com.darekzon.spring.exhandler.aop.ExceptionHandler" />

Powyższy zapis konfiguruje system tak aby wyłapał wszystkie metody zwracające obiekt "ModelAndView" (czyli zazwyczaj metody mapujące URL-e) i przekazywał je do naszego obiektu, a dokładniej do metody "exceptionHandler" w klasie "ExceptionHandler".
Metody te będą przekazywane w formie obiektu ProceedingJoinPoint posiadającego metodę "proceed" która służy do wywołania domyślnej akcji.

Nasza metoda obsługująca wyjątki wygląda jak poniżej

public ModelAndViewexceptionHandler(ProceedingJoinPoint pjp)throws Throwable{
ModelAndView mav=newModelAndView();
try{
mav=(ModelAndView) pjp.proceed();
}catch(Exceptionae){
mav.addObject("exceptionMessage",ae.getMessage());
mav.setViewName("exception");
}
return mav;
}

Działanie jest proste, wywołaj metodę która może rzucić wyjątek (tj. metodę obsługującą url) i jeśli taki wyjątek zostanie rzucony, złap go, dodaj jego wiadomość do widoku jako 'exceptionMessage' oraz wygeneruj widok o nazwie 'exception'. A co za tym idzie, jeśli nasza metoda kontrolera wyrzuci wyjątek, zostanie on złapany a zamiast niego zostanie zwrócony poprawny obiekt ModelAndView.

Oczywiście zamiast prostej podmiany widoków możemy przekierować użytkownika do innej strony, zapisać logi, czy też wysłać maila.

Działający projekt


Działający projekt można pobrać przeglądać pod adresem https://github.com/darek/spring.3.examples/tree/master/aop-global-exception-handler

Brak komentarzy:

Prześlij komentarz