Ответ 1
Введение
ViewExpiredException
будет вызываться всякий раз, когда для параметра javax.faces.STATE_SAVING_METHOD
установлено значение server
(по умолчанию), и enduser отправляет запрос HTTP POST на представление через <h:form>
с помощью <h:commandLink>
, <h:commandButton>
или <f:ajax>
, а связанное состояние просмотра больше не доступно в сеансе.
Состояние представления идентифицируется как значение скрытого поля ввода javax.faces.ViewState
для <h:form>
. Если метод сохранения состояния установлен в server
, он содержит только идентификатор состояния представления, который ссылается на сериализованное состояние представления в сеансе. Таким образом, по истечении срока действия сеанса по какой-либо причине (либо время ожидания на сервере или на стороне клиента, либо cookie сеанса больше не поддерживается по какой-либо причине в браузере или вызовом HttpSession#invalidate()
на сервере или из-за ошибки сервера с как известно в WildFly), тогда сериализованное состояние представления больше не доступно в сеансе, и enduser получит это исключение. Чтобы понять работу сессии, см. Также Как работают сервлеты? Создание, сеансы, общие переменные и многопоточность.
Также существует ограничение на количество просмотров, которое JSF будет хранить в сеансе. Когда предел будет удачен, будет уменьшено время последнего использования. См. Также com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews.
Когда метод сохранения состояния установлен в client
, скрытое поле ввода javax.faces.ViewState
содержит вместо этого все сериализованное состояние представления, поэтому конечный пользователь не получит ViewExpiredException
, когда истекает срок действия сеанса. Однако это может происходить и в среде кластера ( "ОШИБКА: MAC не проверяется" является симптоматичной) и/или когда существует определенный тайм-аут, зависящий от реализации, в состоянии клиентской стороны и/или когда сервер повторно генерирует ключ AES во время перезапуска, см. также Получение ViewExpiredException в кластерной среде, в то время как для метода сохранения состояния установлено значение клиент и сеанс пользователя. Как его решить.
Независимо от решения, убедитесь, что вы используете не enableRestoreView11Compatibility
. он вовсе не восстанавливает исходное состояние представления. Он в основном воссоздает представление и все связанные с ним видимые области beans с нуля и тем самым теряют все исходные данные (состояние). Поскольку приложение будет вести себя запутанным образом ( "Эй, где мои входные значения...?" ), Это очень плохо для пользователей. Вместо этого лучше использовать представления без учета состояния или <o:enableRestorableView>
, чтобы вы могли управлять им только в определенном представлении, а не во всех представлениях.
Что касается того, почему JSF необходимо сохранить состояние просмотра, перейдите к этому ответу: Почему JSF сохраняет состояние компонентов пользовательского интерфейса на сервере?
Предотвращение ViewExpiredException при навигации по страницам
Чтобы избежать ViewExpiredException
, когда, например, когда после сохранения состояния установлено значение server
, перенаправление запроса POST после выхода из системы недостаточно. Вам также необходимо указать браузеру не кэшировать динамические страницы JSF, иначе браузер может отображать их из кеша вместо запроса нового с сервера при отправке на него запроса GET (например, кнопкой "Назад" ).
Скрытое поле javax.faces.ViewState
кэшированной страницы может содержать значение идентификатора состояния представления, которое больше недействительно в текущем сеансе. Если вы (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), вам лучше всего сделать вид безстоящим (см. Раздел ниже) или отправить перенаправление после POST (см. Предыдущий раздел).
Наличие ViewExpiredException
на странице обновления в конфигурации по умолчанию - очень редкий случай. Это может произойти только тогда, когда будет ограничено ограничение на количество просмотров JSF в сеансе. Таким образом, это произойдет только в том случае, если вы вручную установили слишком низкий лимит или что вы постоянно создаете новые представления в фоновом режиме (например, с помощью плохо проведенного опроса). См. Также com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews. Другая причина заключается в том, что дублирующие JSF-библиотеки в пуле классов выполнения конфликтуют друг с другом. Правильная процедура установки JSF описана в нашей странице вики JSF.
Обработка ViewExpiredException
Если вы хотите обработать неотвратимый ViewExpiredException
после действия POST на произвольной странице, которая была уже открыта в какой-либо вкладке/окне браузера, когда вы вышли из системы в другой вкладке/окне, то вы хотите указать a error-page
для этого в web.xml
, который переходит на страницу "Ваша сессия застряла". Например.
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/WEB-INF/errorpages/expired.xhtml</location>
</error-page>
При необходимости используйте заголовок метаобновления на странице ошибки, если вы намереваетесь фактически перенаправить дальше на домашнюю страницу или страницу входа.
<!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
в content
представляет количество секунд перед перенаправлением, 0
означает, что это означает "перенаправление немедленно", вы можете использовать, например, 3
, чтобы браузер мог ждать 3 секунды с переадресацией)
Обратите внимание, что для обработки исключений во время запросов ajax требуется специальный ExceptionHandler
. См. Также Тайм-аут сеанса и обработка ViewExpiredException в JSF/PrimeFaces ajax-запросе. Вы можете найти живой пример на странице OmniFaces FullAjaxExceptionHandler
витрины (это также охватывает запросы без аякса).
Также обратите внимание, что ваша "общая" страница ошибки должна отображаться на <error-code>
из 500
вместо <exception-type>
, например. java.lang.Exception
или java.lang.Throwable
, в противном случае все исключения, заключенные в ServletException
, такие как ViewExpiredException
, по-прежнему будут отображаться на общей странице ошибок. См. Также ViewExpiredException, показанное на странице java.lang.Throwable error-страницы в web.xml.
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errorpages/general.xhtml</location>
</error-page>
Представления безгражданья
Совершенно другая альтернатива - запуск представлений JSF в режиме без состояния. Таким образом, ничто из состояния JSF не будет сохранено, и представления никогда не истекут, а просто будут восстановлены с нуля при каждом запросе. Вы можете включить представления без состояния, установив атрибут transient
<f:view>
в true
:
<f:view transient="true">
</f:view>
Таким образом, скрытое поле javax.faces.ViewState
получит фиксированное значение "stateless"
в Мохарре (не проверил MyFaces в этой точке). Обратите внимание, что эта функция была представлена в Mojarra 2.1.19 и 2.2.0 и недоступна в более старых версиях.
Следствием этого является то, что вы больше не можете использовать область видимости beans. Теперь они будут вести себя как область запроса beans. Один из недостатков заключается в том, что вы должны отслеживать состояние самостоятельно, играя со скрытыми входами и/или свободными параметрами запроса. В основном будут затронуты те формы с полями ввода с атрибутами rendered
, readonly
или disabled
, которые управляются событиями ajax.
Обратите внимание, что <f:view>
необязательно должен быть уникальным во всем представлении и/или находиться только в главном шаблоне. Он также полностью законен для повторного использования и размещения в шаблоне клиента. В основном он "расширяет" родительский <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>
, чтобы сделать его условным. Обратите внимание, что это применимо ко всему представлению не только для вложенного содержимого, например, <h:form>
в приведенном выше примере.
См. также
- ViewExpiredException, показанное на странице java.lang.Throwable error-страницы в web.xml
- Проверьте, существует ли сеанс JSF.
- Тайм-аут сеанса и обработка ViewExpiredException по запросу ajax JSF/PrimeFaces
Несвязанный к конкретной проблеме, использование 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" />