Wprowadzenie
ViewExpiredException
Zostanie wyrzucony, gdy javax.faces.STATE_SAVING_METHOD
jest ustawiony na server
(domyślnie), a Użytkownik końcowy wysyła żądanie HTTP POST w widoku poprzez <h:form>
z <h:commandLink>
, <h:commandButton>
lub <f:ajax>
, gdy stan związany widok nie jest dostępny w sesji już.
Stan widoku jest identyfikowany jako wartość pola ukrytego wejścia javax.faces.ViewState
z <h:form>
. Gdy metoda zapisywania stanu jest ustawiona na server
, zawiera tylko identyfikator stanu widoku, który odwołuje się do serializowanego stanu widoku w sesji. Tak więc, gdy sesja wygaśnie z jakiegoś powodu (przekroczono limit czasu po stronie serwera lub klienta, lub plik cookie sesji nie jest już przechowywany z jakiegoś powodu w przeglądarce lub przez wywołanie HttpSession#invalidate()
serwera lub z powodu błędu specyficznego dla serwera z plikami cookie sesji jako znany w WildFly ), to stan widoku serializowanego nie jest już dostępny w sesji i użytkownik końcowy otrzyma ten wyjątek. Aby zrozumieć działanie sesji, zobacz także Jak działają serwlety? Tworzenie instancji, sesje, zmienne współdzielone i wielowątkowość .
Istnieje również ograniczenie liczby wyświetleń, które JSF będzie przechowywać w sesji. Gdy limit zostanie osiągnięty, najdłużej używany widok wygaśnie. Zobacz także com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews .
Po ustawieniu metody zapisywania stanu na client
, javax.faces.ViewState
ukryte pole wejściowe zawiera zamiast tego cały stan widoku serializowanego, więc użytkownik końcowy nie otrzyma ViewExpiredException
po wygaśnięciu sesji. Może się to jednak zdarzyć w środowisku klastra („BŁĄD: nie zweryfikowano adresu MAC” jest symptomatyczne) i / lub gdy po skonfigurowaniu stanu po stronie klienta występuje limit czasu specyficzny dla implementacji i / lub gdy serwer ponownie generuje klucz AES podczas ponownego uruchamiania zobacz także Uzyskiwanie wyjątku ViewExpiredException w środowisku klastrowym, gdy metoda zapisywania stanu jest ustawiona na klienta, a sesja użytkownika jest prawidłowa, jak go rozwiązać.
Niezależnie od rozwiązania upewnij się, że nie używasz enableRestoreView11Compatibility
. w ogóle nie przywraca pierwotnego stanu widoku. Zasadniczo odtwarza widok i wszystkie powiązane komponenty bean o zasięgu widoku od zera, tracąc w ten sposób wszystkie oryginalne dane (stan). Ponieważ aplikacja będzie zachowywać się w mylący sposób („Hej, gdzie są moje wartości wejściowe… ??”), jest to bardzo niekorzystne dla użytkownika. Lepiej używaj widoków bezstanowych lub <o:enableRestorableView>
zamiast tego możesz zarządzać nim tylko w określonym widoku zamiast we wszystkich widokach.
Jeśli chodzi o dlaczego JSF musi zapisywać stan widoku, przejdź do odpowiedzi: Dlaczego JSF zapisuje stan komponentów UI na serwerze?
Unikanie ViewExpiredException podczas nawigacji po stronie
Aby uniknąć ViewExpiredException
np. Nawigacji powrotnej po wylogowaniu, kiedy ustawiono zapis stanu server
, samo przekierowanie żądania POST po wylogowaniu nie jest wystarczające. Musisz także poinstruować przeglądarkę, aby nie buforowała dynamicznych stron JSF w pamięci podręcznej, w przeciwnym razie przeglądarka może wyświetlać je z pamięci podręcznej zamiast żądać nowej z serwera, gdy wysyłasz żądanie GET (np. Za pomocą przycisku Wstecz).
javax.faces.ViewState
Pole ukryte w pamięci podręcznej może zawierać wartość identyfikatora widok członkowskich, które nie obowiązują już w bieżącej sesji. Jeśli (ab) używasz POST (linki / przyciski poleceń) zamiast GET (zwykłe linki / przyciski) do nawigacji między stronami i klikniesz taki link / przycisk polecenia na stronie w pamięci podręcznej, to z kolei nie uda się z ViewExpiredException
.
Aby uruchomić przekierowanie po wylogowaniu w JSF 2.0, dodaj <redirect />
do <navigation-case>
pytania (jeśli istnieje) lub dodaj ?faces-redirect=true
do outcome
wartości.
<h:commandButton value="Logout" action="logout?faces-redirect=true" />
lub
public String logout() {
// ...
return "index?faces-redirect=true";
}
Aby poinstruować przeglądarkę, aby nie buforowała dynamicznych stron JSF, utwórz plik, Filter
który jest mapowany na nazwę serwletu FacesServlet
i dodaje potrzebne nagłówki odpowiedzi, aby wyłączyć pamięć podręczną przeglądarki. Na przykład
@WebFilter(servletNames={"Faces Servlet"}) // Must match <servlet-name> of your FacesServlet.
public class NoCacheFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse res = (HttpServletResponse) response;
if (!req.getRequestURI().startsWith(req.getContextPath() + ResourceHandler.RESOURCE_IDENTIFIER)) { // Skip JSF resources (CSS/JS/Images/etc)
res.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
res.setHeader("Pragma", "no-cache"); // HTTP 1.0.
res.setDateHeader("Expires", 0); // Proxies.
}
chain.doFilter(request, response);
}
// ...
}
Unikanie ViewExpiredException podczas odświeżania strony
Aby uniknąć ViewExpiredException
odświeżania bieżącej strony, gdy zapisywanie stanu jest ustawione na server
, musisz nie tylko upewnić się, że nawigacja między stronami odbywa się wyłącznie za pomocą GET (zwykłe linki / przyciski), ale także upewnij się, że że do przesyłania formularzy używasz wyłącznie Ajax. Jeśli i tak przesyłasz formularz synchronicznie (bez AJAX), to najlepiej byłoby albo uczynić widok bezstanowym (zobacz dalszą sekcję), albo wysłać przekierowanie po POST (zobacz poprzednią sekcję).
Mając ViewExpiredException
na stronie odświeżanie jest w domyślnej konfiguracji to bardzo rzadki przypadek. Może się to zdarzyć tylko wtedy, gdy zostanie osiągnięty limit ilości wyświetleń, które JSF będzie przechowywać w sesji. Tak więc stanie się to tylko wtedy, gdy ręcznie ustawisz ten limit zbyt nisko, lub jeśli ciągle tworzysz nowe widoki w "tle" (np. Przez źle zaimplementowaną ankietę Ajax na tej samej stronie lub przez źle zaimplementowaną 404 strona błędu na uszkodzonych obrazach tej samej strony). Zobacz także com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews, aby uzyskać szczegółowe informacje na temat tego limitu. Inną przyczyną jest konflikt zduplikowanych bibliotek JSF w ścieżce klas środowiska wykonawczego. Prawidłową procedurę instalacji JSF przedstawiono na naszej stronie wiki JSF .
Obsługa wyjątku ViewExpiredException
Jeśli chcesz obsłużyć nieuniknione ViewExpiredException
po akcji POST na dowolnej stronie, która była już otwarta w jakiejś karcie / oknie przeglądarki, gdy jesteś wylogowany z innej karty / okna, to chcesz określić error-page
dla tego, w web.xml
którym idzie do strony „Przekroczono limit czasu sesji”. Na przykład
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/WEB-INF/errorpages/expired.xhtml</location>
</error-page>
W razie potrzeby użyj nagłówka meta odświeżania na stronie błędu, na wypadek gdybyś zamierzał przekierować dalej na stronę główną lub stronę logowania.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Session expired</title>
<meta http-equiv="refresh" content="0;url=#{request.contextPath}/login.xhtml" />
</head>
<body>
<h1>Session expired</h1>
<h3>You will be redirected to login page</h3>
<p><a href="#{request.contextPath}/login.xhtml">Click here if redirect didn't work or when you're impatient</a>.</p>
</body>
</html>
( 0
in content
reprezentuje ilość sekund przed przekierowaniem, 0
co oznacza "przekierowanie natychmiast", możesz użyć np. 3
aby przeglądarka czekała 3 sekundy z przekierowaniem)
Zauważ, że obsługa wyjątków podczas żądań Ajax wymaga specjalnego ExceptionHandler
. Zobacz także przekroczenie limitu czasu sesji i obsługa wyjątków ViewExpiredException dla żądania AJAX JSF / PrimeFaces . Przykład na żywo można znaleźć na stronie prezentacji OmniFacesFullAjaxExceptionHandler
(dotyczy to również żądań spoza AJAX ).
Należy również pamiętać, że „ogólne”, strona błędu powinien być odwzorowany na <error-code>
od 500
Zamiast <exception-type>
EG java.lang.Exception
lub java.lang.Throwable
, w przeciwnym razie wszystkie wyjątki opakowane w ServletException
taki jak ViewExpiredException
będzie nadal kończy się w ogólnym strony błędu. Zobacz także wyjątek ViewExpiredException pokazany na stronie błędu java.lang.Throwable w pliku web.xml .
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errorpages/general.xhtml</location>
</error-page>
Poglądy bezpaństwowe
Zupełnie inną alternatywą jest uruchamianie widoków JSF w trybie bezstanowym. W ten sposób nic ze stanu JSF nie zostanie zapisane, a widoki nigdy nie wygasną, ale będą po prostu odbudowywane od podstaw na każde żądanie. Możesz włączyć bezstanowe widoki, ustawiając transient
atrybut <f:view>
na true
:
<f:view transient="true">
</f:view>
W ten sposób javax.faces.ViewState
ukryte pole otrzyma stałą wartość "stateless"
w Mojarra (nie sprawdzałem w tym momencie MyFaces). Zwróć uwagę, że ta funkcja została wprowadzona w Mojarra 2.1.19 i 2.2.0 i nie jest dostępna w starszych wersjach.
W konsekwencji nie możesz już używać fasoli z zakresem widoku. Będą teraz zachowywać się jak fasola o zasięgu żądania. Jedną z wad jest to, że musisz samodzielnie śledzić stan, bawiąc się ukrytymi danymi wejściowymi i / lub luźnymi parametrami żądania. Głównie te formularze z polami wejściowymi z rendered
, readonly
lub disabled
będą miały wpływ atrybutów, które są kontrolowane przez ajax zdarzeń.
Zwróć uwagę, że <f:view>
niekoniecznie musi być niepowtarzalny w całym widoku i / lub znajdować się tylko w szablonie głównym. Całkowicie legalne jest również ponowne zadeklarowanie i zagnieżdżenie go w kliencie szablonu. Zasadniczo wtedy „rozszerza” rodzica <f:view>
. Np. W szablonie głównym:
<f:view contentType="text/html">
<ui:insert name="content" />
</f:view>
oraz w kliencie szablonu:
<ui:define name="content">
<f:view transient="true">
<h:form>...</h:form>
</f:view>
</f:view>
Możesz nawet zawinąć <f:view>
w a, <c:if>
aby był warunkowy. Zwróć uwagę, że będzie to dotyczyło całego widoku, a nie tylko zagnieżdżonej zawartości, takiej jak <h:form>
w powyższym przykładzie.
Zobacz też
Bez związku z konkretnym problemem, używanie HTTP POST do czystej nawigacji między stronami nie jest zbyt przyjazne dla użytkownika / SEO. W JSF 2.0 naprawdę powinieneś preferować <h:link>
lub <h:button>
od <h:commandXxx>
tych, które zapewniają zwykłą, waniliową nawigację ze strony do strony.
Czyli zamiast np
<h:form id="menu">
<h:commandLink value="Foo" action="foo?faces-redirect=true" />
<h:commandLink value="Bar" action="bar?faces-redirect=true" />
<h:commandLink value="Baz" action="baz?faces-redirect=true" />
</h:form>
lepiej zrób
<h:link value="Foo" outcome="foo" />
<h:link value="Bar" outcome="bar" />
<h:link value="Baz" outcome="baz" />
Zobacz też
?faces-redirect=true
dooutcome
. Odpowiednio zaktualizowałem odpowiedź.Czy próbowałeś dodać poniższe wiersze do swojego
web.xml
?Okazało się, że jest to bardzo skuteczne, gdy napotkałem ten problem.
źródło
<o:enableRestorableView>
zamiast parametru kontekstowego dla całej aplikacji.Przed zmianą pliku web.xml musisz najpierw upewnić się, że Twój ManagedBean
implements Serializable
:Zwłaszcza jeśli używasz MyFaces
źródło
Unikaj formularzy wieloczęściowych w Richfaces:
Jeśli używasz Richfaces, odkryłem, że żądania Ajax wewnątrz formularzy wieloczęściowych zwracają nowy identyfikator widoku przy każdym żądaniu.
Jak debugować:
Przy każdym żądaniu ajax zwracany jest identyfikator widoku, to jest w porządku, o ile identyfikator widoku jest zawsze taki sam. Jeśli otrzymujesz nowy identyfikator widoku przy każdym żądaniu, oznacza to problem i należy go naprawić.
źródło
Możesz użyć własnego niestandardowego AjaxExceptionHandler lub primefaces-extensions
Zaktualizuj swój face-config.xml
Dodaj następujący kod na swojej stronie jsf
źródło
Otrzymałem ten błąd: javax.faces.application.ViewExpiredException. Kiedy korzystałem z różnych żądań, znalazłem te, które mają ten sam JsessionId, nawet po ponownym uruchomieniu serwera. Wynika to więc z pamięci podręcznej przeglądarki. Po prostu zamknij przeglądarkę i spróbuj, zadziała.
źródło
Gdy nasza strona jest bezczynna przez x okres czasu, widok wygaśnie i wyrzuci javax.faces.application.ViewExpiredException, aby temu zapobiec, jednym rozwiązaniem jest utworzenie CustomViewHandler, który rozszerza ViewHandler i zastępuje metodę restoreView, wszystkie inne metody są delegowane do Rodzic
Następnie musisz dodać go do swojego faces-config.xml
Dzięki za oryginalną odpowiedź na poniższy link: http://www.gregbugaj.com/?p=164
źródło
Dodaj tę linię do swojego pliku web.xml. U mnie działa
źródło
Sam napotkałem ten problem i zdałem sobie sprawę, że jest to efekt uboczny utworzonego przeze mnie filtru, który filtrował wszystkie żądania w aplikacji. Jak tylko zmodyfikowałem filtr, aby wybierać tylko niektóre żądania, ten problem nie wystąpił. Może warto sprawdzić takie filtry w swojej aplikacji i zobaczyć, jak się zachowują.
źródło
Dodałem następującą konfigurację do web.xml i został rozwiązany.
źródło