класса javax.сталкиваться.приложение.ViewExpiredException: не удалось восстановить представление

Я написал простое приложение с безопасностью, управляемой контейнером. Проблема в том, что когда я вхожу в систему и открываю другую страницу, на которой я выхожу из системы, я возвращаюсь на первую страницу и нажимаю на любую ссылку и т. д. или обновляю страницу, Я получаю это исключение. Я думаю, это нормально (или, может быть, нет:)), потому что я вышел из системы и сеанс уничтожен. Что делать, чтобы перенаправить пользователя, например, на индекс.XHTML или логин.xhtml и спасти его от просмотра этой страницы/сообщения об ошибке?

другими словами, как я могу автоматически перенаправлять другие страницы на страницу индекса / входа после выхода из системы?

вот это:

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)

10 ответов


введение

на ViewExpiredException будет брошен всякий раз, когда javax.faces.STATE_SAVING_METHOD установлено значение server (по умолчанию), и пользователь отправляет запрос HTTP POST на просмотр через <h:form> С <h:commandLink>, <h:commandButton> или <f:ajax>, в то время как связанное состояние представления больше не доступно в сеансе.

Просмотр состояния определяется как значение скрытого поля ввода javax.faces.ViewState на <h:form>. С помощью метода сохранения состояния установлено значение server, это содержит только ID состояния представления который ссылается на состояние сериализованного представления в сеансе. Таким образом, когда сеанс по какой-либо причине истек (либо тайм-аут на стороне сервера или клиента, либо cookie сеанса больше не поддерживается по какой-либо причине в браузере, или путем вызова HttpSession#invalidate() в сервере, или из-за специфической ошибки сервера с сеансовыми куки, как известно в WildFly), то состояние сериализованного представления больше не доступно в сеансе, и пользователь получит это исключение. Чтобы понять работу сеанс, см. Также как работают сервлеты? Создание экземпляров, сеансы, общие переменные и многопоточность.

существует также ограничение на количество просмотров, которые JSF будет хранить в сеансе. Когда предел будет достигнут, то наименее недавно использованное представление будет истекло. См. также com.солнце.сталкиваться.numberOfViewsInSession vs com.солнце.сталкиваться.numberOfLogicalViews.

с помощью метода сохранения состояния client, the javax.faces.ViewState скрытое поле ввода вместо этого содержит все состояние сериализованного представления, поэтому конечный пользователь не получит ViewExpiredException когда сессия истекает. Однако это может произойти в кластерной среде ("ошибка: MAC не проверил" симптоматично) и/или когда на стороне клиента настроено время ожидания реализации и/или когда сервер повторно генерирует ключ AES во время перезапуска, см. Также получение исключения ViewExpiredException в кластеризованной среде, когда метод сохранения состояния установлен в client и User session действует как ее решить.

независимо от решения, убедитесь, что вы делаете не использовать enableRestoreView11Compatibility. он вообще не восстанавливает исходное состояние представления. Он в основном воссоздает представление и все связанные с ним бобы с областью видимости с нуля и таким образом теряет все исходные данные (состояние). Как приложение будет вести себя странным образом ("Эй, где мои входные значения..??"), это очень плохо для пользователей. Лучше использовать представления без состояния или <o:enableRestorableView> а не так вы можете управлять им только в определенном представлении, а не во всех представлениях.

как к почему JSF необходимо сохранить состояние просмотра, перейдите к этому ответу:почему JSF сохраняет состояние компонентов пользовательского интерфейса на сервере?

избегая ViewExpiredException на странице навигации

во избежание ViewExpiredException когда, например, переход назад после выхода из системы, когда сохранение состояния установлено в server, только перенаправление запроса POST после выхода из системы не достаточный. Вам также необходимо указать браузеру не кэш динамических страниц JSF, в противном случае браузер может показать их из кэша вместо запроса нового с сервера при отправке запроса GET на нем (например, кнопкой "Назад").

на javax.faces.ViewState скрытое поле кэшированной страницы может содержать значение ID состояния представления, которое больше не является допустимым в текущем сеансе. Если вы (ab)используете POST (командные ссылки/кнопки) вместо GET (обычный ссылки / кнопки) для навигации по страницам и нажмите такую командную ссылку/кнопку на кэшированной странице, тогда это, в свою очередь, завершится неудачей с ViewExpiredException.

огонь редирект после выхода в JSF 2.0, либо добавить <redirect /> до <navigation-case> в вопросе (если есть), или добавить ?faces-redirect=true до outcome значение.

<h:commandButton value="Logout" action="logout?faces-redirect=true" />

или

public String logout() {
    // ...
    return "index?faces-redirect=true";
}

чтобы проинструктировать браузер не кэшировать динамические страницы JSF, создайте Filter который отображается на имя сервлета FacesServlet и добавляет необходимые заголовки ответа, чтобы отключить кэш браузера. Е. Г.

@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);
    }

    // ...
}

избегая исключения ViewExpiredException на странице обновления

во избежание ViewExpiredException при обновлении текущей страницы, когда сохранение состояния установлено в server, вам не только нужно убедиться, что вы выполняете переход от страницы к странице исключительно с помощью GET (обычные ссылки/кнопки), но вам также необходимо убедиться, что вы используете ajax исключительно для отправки форм. Если вы отправка формы синхронно (не ajax) в любом случае, тогда вам лучше либо сделать вид без состояния (см. Более поздний раздел), либо отправить перенаправление после публикации (см. предыдущий раздел).

имеющего ViewExpiredException при обновлении страницы в конфигурации по умолчанию очень редкий случай. Это может произойти только тогда, когда ограничение на количество просмотров JSF будет сохранено в сеансе. Таким образом, это произойдет только тогда, когда вы вручную установили этот предел слишком низко или постоянно создаете новые представления в справочная информация (например, плохо проведенный опрос). См. также com.солнце.сталкиваться.numberOfViewsInSession vs com.солнце.сталкиваться.numberOfLogicalViews. Другой причиной является наличие дубликатов библиотек JSF в пути к классам среды выполнения, конфликтующих друг с другом. Правильная процедура установки JSF описана в наша страница JSF wiki.

Обработка ViewExpiredException

когда вы хотите справиться с неизбежным ViewExpiredException после действия POST на произвольной странице, которая был уже открыт в какой-то вкладке/окне браузера, когда вы вышли из системы в другой вкладке / окне, затем вы хотите указать error-page для этого в web.xml который переходит на страницу" ваш сеанс тайм-аут". Е. Г.

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

при необходимости используйте заголовок meta refresh на странице ошибок, если вы действительно собираетесь редирект далее к домашней или странице входа в систему.

<!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>

(the 0 на content представляет количество секунд до редирект, 0 таким образом, означает "перенаправить немедленно", вы можете использовать, например,3 чтобы браузер подождал 3 секунды с перенаправлением)

обратите внимание, что обработка исключений во время AJAX-запросов требует специального ExceptionHandler. См. также тайм-аут сеанса и обработка ViewExpiredException в запросе ajax JSF/PrimeFaces. Вы можете найти живой пример в OmniFaces FullAjaxExceptionHandler витрина-страницы (это также охватывает не ajax требования заказчика.)

также обратите внимание, что ваша страница" общая " ошибка должна быть отображена на <error-code> of 500 вместо <exception-type>, например,java.lang.Exception или java.lang.Throwable, в противном случае все исключения завернуты в ServletException например ViewExpiredException равно в конечном итоге в общей страницу ошибки. См. также ViewExpiredException показано в java.ленг.Throwable error-страница в интернете.в XML.

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

вид без гражданства

совершенно другой альтернативой является запуск JSF представления в режиме без состояния. Таким образом ничего Государственного в JSF будет сохранен и мнения никогда не истекает, а просто заново при каждом запросе. Вы можете включить представления без состояния, установив на <f:view> to true:

<f:view transient="true">

</f:view>

сюда javax.faces.ViewState скрытое поле получит фиксированное значение "stateless" в Mojarra (не проверено MyFaces на данный момент). Обратите внимание, что эта функция была ввел в Mojarra 2.1.19 и 2.2.0 и не доступно в более старых версиях.

следствием является невозможность использования просмотреть больше уровня фасоли. Теперь они будут вести себя, как бобы с просьб. Одним из недостатков является то, что вы должны отслеживать состояние самостоятельно, играя со скрытыми входами и/или параметрами запроса. В основном те формы с полями ввода с rendered, readonly или disabled будут затронуты атрибуты, управляемые событиями ajax.

отметим, что <f:view> не обязательно должен быть уникальным во всем представлении и / или находиться только в главном шаблоне. Это также полностью законно, чтобы redeclare и вложить его в клиент шаблона. Он в основном "расширяет" родителя <f:view> затем. Е. Г. в мастер-шаблоне:

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

и в шаблоне клиента:

<ui:define name="content">
    <f:view transient="true">
        <h:form>...</h:form>
    </f:view>
</f:view>

вы даже можете обернуть <f:view> на <c:if> чтобы сделать его условным. Обратите внимание, что это будет применяться на весь просмотр не только вложенного содержимого, например the <h:form> в приведенном выше примере.

см. также


связаны к конкретной проблеме, используя HTTP POST для чистого страница-для-страницы навигации не очень пользователей/SEO дружественных. В JSF 2.0 вы действительно должны предпочесть <h:link> или <h:button> на <h:commandXxx> для простой ванильной навигации по страницам.

так вместо, например,

<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>

лучше сделать

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

см. также


вы пытались добавить строки ниже к вашему web.xml?

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

я обнаружил, что это очень эффективно, когда я столкнулся с этой проблемой.


Первое, что вы должны сделать, прежде чем менять web.в XML убедитесь, что ManagedBean implements Serializable:

@ManagedBean
@ViewScoped
public class Login implements Serializable {
}

особенно если вы используете MyFaces


избегайте составных форм в Richfaces:

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

Если вы используете Richfaces, я обнаружил, что ajax-запросы внутри составных форм возвращают новый идентификатор представления для каждого запроса.

как отлаживать:

при каждом запросе ajax возвращается идентификатор представления, что нормально, если идентификатор представления всегда один и тот же. Если вы получаете новый идентификатор представления по каждому запросу, то возникает проблема и ее необходимо устранить.


вы можете использовать свой собственный AjaxExceptionHandler или primefaces-extensions

обновите faces-config.в XML

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

добавьте следующий код на страницу jsf

...
<pe:ajaxErrorHandler />
...

Я получал эту ошибку: javax.сталкиваться.приложение.ViewExpiredException.Когда я использую разные запросы, я нашел те, которые имеют тот же JsessionId, даже после перезагрузки сервера. Так это из-за кэша браузера. Просто закройте браузер и попробуйте, он будет работать.


когда наша страница простаивает в течение x времени, представление истекает и выбрасывает javax.сталкиваться.приложение.ViewExpiredException, чтобы предотвратить это одним из решений является создание CustomViewHandler, который расширяет ViewHandler и переопределить метод restoreView все остальные методы делегируются родительскому

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;
    }

}   

затем вам нужно добавить его в faces-config.в XML

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

Спасибо за оригинальный ответ ниже ссылка на сайт: http://www.gregbugaj.com/?p=164


пожалуйста, добавьте эту строку в ваш веб.XML Это работает для меня

<context-param>
        <param-name>org.ajax4jsf.handleViewExpiredOnClient</param-name> 
        <param-value>true</param-value>     
    </context-param>

Я сам столкнулся с этой проблемой и понял, что это из-за побочного эффекта фильтра, который я создал, который фильтровал все запросы на применение. Как только я изменил фильтр, чтобы выбрать только определенные запросы, эта проблема не возникла. Возможно, неплохо проверить наличие таких фильтров в вашем приложении и посмотреть, как они себя ведут.


Я добавляю следующую конфигурацию в web.в XML и это разрешилось.

<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>