javax.twarze.podanie.ViewExpiredException: nie można przywrócić widoku

Napisałem prostą aplikację z zabezpieczeniami zarządzanymi kontenerami. Problem polega na tym, że gdy loguję się i otwieram kolejną stronę na której się wylogowuję, to wracam do pierwszej strony i klikam na dowolny link itp lub odświeżam stronę dostaję ten wyjątek. To chyba normalne (a może nie:)) bo się wylogowałem i sesja jest zniszczona. Co zrobić, aby przekierować użytkownika do np. indeksu.xhtml lub login.xhtml i uchronić go przed zobaczeniem tej strony/wiadomości o błędzie?

Innymi słowy jak Mogę automatyczne przekierowanie innych stron do strony indeksu/logowania po wylogowaniu?

Oto jest:

javax.faces.application.ViewExpiredException: viewId:/index.xhtml - View /index.xhtml could not be restored.
    at com.sun.faces.lifecycle.RestoreViewPhase.execute(RestoreViewPhase.java:212)
    at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101)
    at com.sun.faces.lifecycle.RestoreViewPhase.doPhase(RestoreViewPhase.java:110)
    at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118)
    at javax.faces.webapp.FacesServlet.service(FacesServlet.java:312)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1523)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:343)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
    at filter.HttpHttpsFilter.doFilter(HttpHttpsFilter.java:66)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:256)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:215)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:277)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:188)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:641)
    at com.sun.enterprise.web.WebPipeline.invoke(WebPipeline.java:97)
    at com.sun.enterprise.web.PESessionLockingStandardPipeline.invoke(PESessionLockingStandardPipeline.java:85)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:185)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:325)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:226)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:165)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:791)
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:693)
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:954)
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:170)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:135)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:102)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:88)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:76)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:53)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:57)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:69)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:330)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:309)
    at java.lang.Thread.run(Thread.java:619)
Author: BalusC, 2010-09-04

10 answers

Wprowadzenie

ViewExpiredException zostanie wyrzucone, gdy {[12] } jest ustawione na server (domyślnie), a użytkownik końcowy wyśle żądanie HTTP POST w widoku przez <h:form> z <h:commandLink>, <h:commandButton> lub <f:ajax>, podczas gdy skojarzony stan widoku nie jest już dostępny w sesji.

Stan widoku jest identyfikowany jako wartość ukrytego pola wejściowego {[18] } z <h:form>. Przy metodzie zapisywania stanu ustawionej na server, zawiera ona tylko identyfikator stanu widoku, który odwołuje się do serializowanego stanu widoku w sesja. Tak więc, gdy sesja wygasła z jakiegoś powodu (albo timed out Po stronie serwera lub klienta, lub plik cookie sesji nie jest już utrzymywany z jakiegoś powodu w przeglądarce, lub przez wywołanie HttpSession#invalidate() na serwerze, lub z powodu błędu specyficznego dla serwera z plikami cookie sesji, jak wiadomo w WildFly {78]}), wtedy serializowany stan widoku nie jest już dostępny w sesji, a użytkownik końcowy otrzyma ten wyjątek. Aby zrozumieć działanie sesji, Zobacz także jak działają serwlety? Tworzenie instancji, sesje, współdzielone zmienne i wielowątkowość .

Istnieje również limit liczby wyświetleń przechowywanych przez JSF w sesji. Gdy limit zostanie osiągnięty, to ostatnio używany widok straci ważność. Zob.również com.słońce.twarze.numerofviewsinsession vs com.słońce.twarze.numeroflogicalviews .

Z metodą zapisywania stanu ustawioną na client, ukryte pole wejściowe javax.faces.ViewState Zawiera zamiast tego cały serializowany stan widoku, więc użytkownik końcowy nie otrzyma ViewExpiredException po zakończeniu sesji. Może się to jednak zdarzyć w środowisku klastrowym ("ERROR: MAC did not verify" jest objawowe) i/lub gdy po skonfigurowaniu stanu po stronie klienta jest określony limit czasu dla danej implementacji i/lub gdy serwer ponownie generuje klucz AES podczas restartu, Zobacz także Getting ViewExpiredException in clustered environment while State saving method is set to client and user session is valid jak go rozwiązać.

Niezależnie od rozwiązania, upewnij się, że Nie użyj enableRestoreView11Compatibility. w ogóle nie przywraca oryginalnego stanu widoku. Zasadniczo odtwarza widok i wszystkie powiązane z nim zakresy od zera, tracąc w ten sposób wszystkie oryginalne dane (stan). Ponieważ aplikacja będzie zachowywać się w sposób mylący ("Hey, where are my input values..??"), jest to bardzo złe dla doświadczenia użytkownika. Lepiej używać bezstanowych widoków lub <o:enableRestorableView>, aby zarządzać nimi tylko w określonym widoku, a nie we wszystkich widokach.

Co do dlaczego JSF musi zapisać stan widoku, przejdź do odpowiedzi: dlaczego JSF zapisuje stan komponentów interfejsu użytkownika na serwerze?

Unikanie ViewExpiredException na nawigacji strony

Aby uniknąć ViewExpiredException, gdy np. powrót po wylogowaniu, gdy zapis stanu jest ustawiony na server, jedynie przekierowanie żądania POST po wylogowaniu nie jest wystarczające. Musisz również poinstruować przeglądarkę, aby Nie buforowała dynamicznych stron JSF, w przeciwnym razie przeglądarka może je wyświetlić z pamięci podręcznej zamiast żądać nowego od serwera, gdy wysyłasz na niego żądanie GET (np. przyciskiem wstecz).

Ukryte pole buforowanej strony może zawierać wartość ID stanu widoku, która nie jest już ważna w bieżącej sesji. Jeśli (ab)używasz POST (linki/przyciski poleceń) zamiast GET (zwykłe linki/przyciski) do nawigacji strona-strona i klikniesz taki link/przycisk polecenia na buforowanej stronie, to to z kolei nie powiedzie się z ViewExpiredException.

Aby odpalić przekierowanie po wylogowaniu w JSF 2.0 Dodaj <redirect /> do <navigation-case> (jeśli istnieje), lub dodaj ?faces-redirect=true do wartości outcome.

<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 Filter, który jest mapowany na nazwie serwletu FacesServlet i dodaje nagłówki odpowiedzi potrzebne do wyłączenia pamięci podręcznej przeglądarki. Np.

@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 przy odświeżaniu strony

W celu uniknięcia ViewExpiredException przy odświeżaniu bieżącej strony, gdy zapisywanie stanu jest ustawione na server, musisz nie tylko upewnić się, że wykonujesz nawigację strona-strona wyłącznie przez GET( zwykłe linki/przyciski), ale także musisz upewnić się, że używasz wyłącznie ajax do przesyłania formularzy. Jeśli i tak przesyłasz formularz synchronicznie( nie-ajax), najlepiej albo uczynić widok bezpaństwowym (patrz późniejsza sekcja), albo wysłać przekierowanie po poście(patrz poprzednia sekcja).

Posiadanie ViewExpiredException przy odświeżaniu strony jest domyślnie konfiguracja bardzo rzadki przypadek. Może się to zdarzyć tylko wtedy, gdy limit liczby wyświetleń przechowywanych przez JSF w sesji zostanie osiągnięty. Tak więc stanie się to tylko wtedy, gdy ręcznie ustawisz zbyt niski limit lub będziesz stale tworzyć nowe widoki w tle (np. przez źle zaimplementowaną ankietę). Zob.również com.słońce.twarze.numerofviewsinsession vs com.słońce.twarze.numeroflogicalviews . Inną przyczyną jest posiadanie zduplikowanych bibliotek JSF w runtime classpath sprzecznych ze sobą. The correct procedura instalacji JSF jest opisana w na naszej stronie wiki JSF .

Obsługa ViewExpiredException

Jeśli chcesz obsłużyć nieuniknione ViewExpiredException po akcji POST na dowolnej stronie, która została już otwarta w jakiejś karcie/oknie przeglądarki, gdy jesteś wylogowany w innej karcie/oknie, następnie chcesz określić error-page dla tego w web.xml, który przechodzi do strony "Twoja sesja jest timed out". Np.

<error-page>
    <exception-type>javax.faces.application.ViewExpiredException</exception-type>
    <location>/WEB-INF/errorpages/expired.xhtml</location>
</error-page>

Użyj w razie potrzeby nagłówka odświeżania meta w błędzie strona w przypadku, gdy zamierzasz faktycznie przekierować dalej do strony głównej lub 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 w content reprezentuje ilość sekund przed przekierowaniem, 0 oznacza to "przekierowanie natychmiast", możesz użyć np. {[46] } aby pozwolić przeglądarce odczekać 3 sekundy z przekierowaniem)

Należy pamiętać, że obsługa wyjątków podczas żądań ajax wymaga specjalnego ExceptionHandler. Zobacz także session timeout i obsługę ViewExpiredException w JSF / PrimeFaces ajax wniosek . Przykład na żywo można znaleźć na OmniFaces FullAjaxExceptionHandler showcase page (dotyczy to również żądań innych niż ajax).

Zauważ również, że Twoja "ogólna" strona błędu powinna być odwzorowana na <error-code> Z 500 zamiast <exception-type> np. java.lang.Exception lub java.lang.Throwable, w przeciwnym razie wszystkie wyjątki zawinięte w ServletException, takie jak ViewExpiredException, będą nadal wyświetlane na ogólnej stronie błędu. Zobacz także ViewExpiredException pokazany w Javie.lang.Throwable error-strona w sieci.xml .

<error-page>
    <error-code>500</error-code>
    <location>/WEB-INF/errorpages/general.xhtml</location>
</error-page>

Stateless wyświetleń

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 po prostu zostaną odbudowane od podstaw na każde żądanie. Możesz włączyć widoki bezstanowe, ustawiając atrybut transient atrybutu <f:view> na true:

<f:view transient="true">

</f:view>

W ten sposób javax.faces.ViewState ukryte pole otrzyma stałą wartość "stateless" W Mojarrze (nie sprawdzałem w tym momencie MyFaces). Zauważ, że ta funkcja została wprowadzona w Mojarra 2.1.19 i 2.2.0 i nie jest dostępna w starszych wersjach.

Konsekwencją jest to, że nie można już używać fasoli z widokiem. Będą teraz zachowywać się jak prośba scoped fasola. Jedną z wad jest to, że musisz śledzić stan samodzielnie, manipulując ukrytymi wejściami i/lub luźnymi parametrami żądania. Głównie te formularze z polami wejściowymi z rendered, readonly lub disabled atrybuty, które są kontrolowane przez zdarzenia ajax będą miały wpływ.

Zauważ, że <f:view> robi niekoniecznie musi być unikalny w całym widoku i / lub znajdować się tylko w szablonie głównym. Jest również całkowicie legalne ponowne zgłoszenie i zagnieżdżenie go w kliencie szablonów. To w zasadzie "rozszerza" rodzica <f:view> wtedy. Np. w szablonie głównym:

<f:view contentType="text/html">
    <ui:insert name="content" />
</f:view>

I w template client:

<ui:define name="content">
    <f:view transient="true">
        <h:form>...</h:form>
    </f:view>
</f:view>
Możesz nawet zawinąć <f:view> w <c:if>, aby uczynić to warunkowym. Zauważ, że będzie to miało zastosowanie do całego widoku , a nie tylko do zagnieżdżonej zawartości, takiej jak <h:form> powyżej przykład.

Zobacz też


Niezależnie od konkretnego problemu, używanie HTTP POST do czystej nawigacji między stronami nie jest zbyt przyjazne dla użytkownika/SEO. W JSF 2.0 powinieneś naprawdę preferować <h:link> lub <h:button> nad <h:commandXxx> do zwykłej waniliowej nawigacji strona-strona.

Więc 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>

Better do

<h:link value="Foo" outcome="foo" />
<h:link value="Bar" outcome="bar" />
<h:link value="Baz" outcome="baz" />

Zobacz też

 313
Author: BalusC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:10:41

Próbowałeś dodać linie poniżej do swojego web.xml?

<context-param>
   <param-name>com.sun.faces.enableRestoreView11Compatibility</param-name>
   <param-value>true</param-value>
</context-param>

Stwierdziłem, że jest to bardzo skuteczne, gdy napotkałem ten problem.

 50
Author: Mike GH,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-30 08:54:54

Najpierw co musisz zrobić, zanim zmienisz www.xml {[4] } ma na celu upewnienie się, że Twoje ManagedBean implements Serializable:

@ManagedBean
@ViewScoped
public class Login implements Serializable {
}

Szczególnie jeśli używasz MyFaces

 5
Author: ZuzEL,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-28 14:58:09

Unikaj form wieloczęściowych w Richfaces:

<h:form enctype="multipart/form-data">
    <a4j:poll id="poll" interval="10000"/>
</h:form>

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, co jest w porządku, o ile identyfikator widoku jest zawsze taki sam. Jeśli przy każdym żądaniu pojawi się nowy identyfikator widoku, występuje problem i należy go naprawić.

 3
Author: tak3shi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-03-24 12:24:55

Możesz użyć własnego AjaxExceptionHandler lub primefaces-extensions

Update your faces-config.xml

...
<factory>
  <exception-handler-factory>org.primefaces.extensions.component.ajaxerrorhandler.AjaxExceptionHandlerFactory</exception-handler-factory>
</factory>
...

Dodaj następujący kod na stronie jsf

...
<pe:ajaxErrorHandler />
...
 0
Author: myset,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-11 10:00:50

Dostałem ten błąd: javax.twarze.podanie.ViewExpiredException.Kiedy używam różnych żądań, znalazłem te, które mają ten sam IDENTYFIKATOR JsessionId, nawet po ponownym uruchomieniu serwera. Wynika to z pamięci podręcznej przeglądarki. Wystarczy zamknąć przeglądarkę i spróbować, to będzie działać.

 0
Author: rahul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-05 14:44:47

Gdy nasza strona będzie bezczynna przez x czas, Widok wygaśnie i wyrzuci javaxa.twarze.podanie.ViewExpiredException aby temu zapobiec jednym z rozwiązań jest stworzenie CustomViewHandler, który rozszerza ViewHandler wszystkie pozostałe metody są delegowane do rodzica

import java.io.IOException;
import javax.faces.FacesException;
import javax.faces.application.ViewHandler;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;

public class CustomViewHandler extends ViewHandler {
    private ViewHandler parent;

    public CustomViewHandler(ViewHandler parent) {
        //System.out.println("CustomViewHandler.CustomViewHandler():Parent View Handler:"+parent.getClass());
        this.parent = parent;
    }

    @Override 
    public UIViewRoot restoreView(FacesContext facesContext, String viewId) {
    /**
     * {@link javax.faces.application.ViewExpiredException}. This happens only  when we try to logout from timed out pages.
     */
        UIViewRoot root = null;
        root = parent.restoreView(facesContext, viewId);
        if(root == null) {
            root = createView(facesContext, viewId);
        }
        return root;
    }

    @Override
    public Locale calculateLocale(FacesContext facesContext) {
        return parent.calculateLocale(facesContext);
    }

    @Override
    public String calculateRenderKitId(FacesContext facesContext) {
        String renderKitId = parent.calculateRenderKitId(facesContext);
        //System.out.println("CustomViewHandler.calculateRenderKitId():RenderKitId: "+renderKitId);
        return renderKitId;
    }

    @Override
    public UIViewRoot createView(FacesContext facesContext, String viewId) {
        return parent.createView(facesContext, viewId);
    }

    @Override
    public String getActionURL(FacesContext facesContext, String actionId) {
        return parent.getActionURL(facesContext, actionId);
    }

    @Override
    public String getResourceURL(FacesContext facesContext, String resId) {
        return parent.getResourceURL(facesContext, resId);
    }

    @Override
    public void renderView(FacesContext facesContext, UIViewRoot viewId) throws IOException, FacesException {
        parent.renderView(facesContext, viewId);
    }

    @Override
    public void writeState(FacesContext facesContext) throws IOException {
        parent.writeState(facesContext);
    }

    public ViewHandler getParent() {
        return parent;
    }

}   

Następnie należy dodać go do faces-config.xml

<application>
    <view-handler>com.demo.CustomViewHandler</view-handler>
</application>

Dzięki za oryginalną odpowiedź na poniższy link: http://www.gregbugaj.com/?p=164

 0
Author: user4924157,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-05-13 06:45:29

Proszę dodać ten wiersz w swojej sieci.xml It works for me

<context-param>
        <param-name>org.ajax4jsf.handleViewExpiredOnClient</param-name> 
        <param-value>true</param-value>     
    </context-param>
 -2
Author: harshal,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-13 15:21:05

Sam napotkałem ten problem i zdałem sobie sprawę, że to z powodu efektu ubocznego stworzonego przeze mnie filtra, który filtrował wszystkie żądania na aplikacji. Jak tylko zmodyfikowałem filtr, aby wybrać tylko niektóre żądania, ten problem nie wystąpił. Być może dobrze jest sprawdzić, czy takie filtry znajdują się w Twojej aplikacji i zobaczyć, jak się zachowują.

 -2
Author: javshak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-25 09:40:26

Dodaję następującą konfigurację do web.xml i został rozwiązany.

<context-param>
    <param-name>com.sun.faces.numberOfViewsInSession</param-name>
    <param-value>500</param-value>
</context-param>
<context-param>
    <param-name>com.sun.faces.numberOfLogicalViews</param-name>
    <param-value>500</param-value>
</context-param>
 -3
Author: Mohamed Ali,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-10 09:50:47