Вступ
ViewExpiredException
Буде викинутий щоразу , коли javax.faces.STATE_SAVING_METHOD
встановлено значення server
( по замовчуванню) і кінцевий користувач надсилає запит HTTP POST на представленні через <h:form>
з <h:commandLink>
, <h:commandButton>
або<f:ajax>
, в той час як зв'язаний стан відображення не доступна на сесії більше.
Стан уявлення ідентифікується як значення прихованого поля введення javax.faces.ViewState
з <h:form>
. Якщо встановлено метод збереження стану server
, він містить лише ідентифікатор стану перегляду, який посилається на стан серіалізованого перегляду в сеансі. Отже, коли сесія з якихось причин закінчилася (або вичерпано на серверній чи клієнтській стороні, або cookie сеансу з певних причин більше не підтримується в браузері, або зателефонувавши HttpSession#invalidate()
на сервер, або через помилку, пов’язану з сервером, із сеансовими файлами cookie як відомо в WildFly ), то стан серіалізованого перегляду більше не доступний в сеансі, і кінцевий користувач отримає це виняток. Щоб зрозуміти роботу сеансу, див. Також Як працюють сервлети? Моменталізація, сеанси, загальні змінні та багатопотоковість .
Існує також обмеження кількості переглядів, які зберігатиме JSF у сеансі. Якщо буде досягнуто обмеження, термін дії найменш використовуваного перегляду закінчиться. Дивіться також com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews .
Якщо встановлено метод збереження стану client
, javax.faces.ViewState
приховане поле введення містить замість цього весь серіалізований стан перегляду, тому ендузер не отримає а, ViewExpiredException
коли сеанс закінчується. Однак це все ще може статися в середовищі кластера ("ПОМИЛКА: MAC не перевірив" є симптоматичним) та / або коли в налаштуваннях стану клієнтської конфігурації є специфічний для впровадження час та / або коли сервер повторно генерує ключ AES під час перезавантаження , див. також Отримання ViewExpiredException в кластерному середовищі, тоді як метод економії стану встановлений для клієнта, а користувацький сеанс є дійсним, як його вирішити.
Незалежно від рішення, переконайтеся, що ви не використовуєте enableRestoreView11Compatibility
. він зовсім не відновлює початковий стан перегляду. Це, в основному, відтворює подання, і всі пов'язані з ним види збивають боби з нуля, і, таким чином, втрачаючи всі вихідні дані (стан). Оскільки додаток буде вести себе заплутано ("Ей, де мої вхідні значення .. ??"), це дуже погано для досвіду користувача. Краще використовувати представлення без стану або <o:enableRestorableView>
замість цього, щоб ви могли керувати ним у певному вікні, а не на всіх.
Щодо того, чому JSF потребує збереження стану перегляду, зверніться до цієї відповіді: Чому JSF зберігає стан компонентів інтерфейсу на сервері?
Уникнення ViewExpiredException на навігації по сторінці
Щоб уникнути, ViewExpiredException
наприклад, повернення назад після виходу, коли встановлено збереження стану, server
недостатньо лише перенаправлення запиту POST після виходу. Вам також потрібно доручити браузеру цього не робити кешувати динамічні сторінки JSF, інакше веб-переглядач може відобразити їх із кеша, а не вимагати свіжого з сервера, коли ви надсилаєте на нього GET-запит (наприклад, кнопкою "назад").
javax.faces.ViewState
Приховані поля кешованих сторінки можуть містити значення стану відображення ідентифікатора , яке не є дійсним більше в поточному сеансі. Якщо ви (ab) використовуєте POST (посилання / кнопки команд) замість GET (звичайні посилання / кнопки) для навігації зі сторінки на сторінку та натискаєте таку командну посилання / кнопку на кешованій сторінці, то це в свою чергу провал з aViewExpiredException
.
Щоб запустити переадресацію після виходу з програми в 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 у сеансі. Отже, це відбудеться лише тоді, коли ви вручну встановите цей обмежений шлях занадто низько, або якщо ви постійно створюєте нові представлення у "фоновому режимі" (наприклад, через погано реалізоване опитування на ajax на тій же сторінці або через погано реалізований 404 сторінка помилок на зламаних зображеннях цієї ж сторінки). Докладніше про цю межу див. У розділі com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews . Ще однією причиною є наявність дублікатів JSF-бібліотек під час виконання класного шляху, що конфліктує між собою. Правильна процедура встановлення JSF описана на нашій вікі-сторінці JSF .
Обробка 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>
Використовуйте, якщо необхідно, заголовок метаоновлення на сторінці помилок у випадку, якщо ви насправді перенаправляєтесь далі на головну сторінку чи сторінку входу.
<!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
представляє кількість секунд перед переадресацією, 0
таким чином означає "негайно переадресувати", ви можете використовувати, наприклад, 3
щоб дозволити браузеру чекати 3 секунди з переадресацією)
Зауважте, що обробка винятків під час запитів ajax вимагає спеціального ExceptionHandler
. Дивіться також таймаут сесії та обробку ViewExpiredException за запитом ajax JSF / PrimeFaces . Ви можете знайти приклад прямого FullAjaxExceptionHandler
перегляду на сторінці демонстрації OmniFaces (це також стосується запитів, які не є ajax).
Також зверніть увагу , що ваш «взагалі» помилка сторінки повинні бути відображені <error-code>
в 500
замість <exception-type>
ЕГ java.lang.Exception
або java.lang.Throwable
, в іншому випадку все виключення , загорнуті в ServletException
таких , як ViewExpiredException
буде по- як і раніше в кінцевому підсумку в загальній сторінці помилки. Див. Також ViewExpiredException, показаний на java.lang.Сторінка помилок у веб.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"
в Mojarra (не перевіряли MyFaces на даний момент). Зауважте, що ця функція була введена в Mojarra 2.1.19 та 2.2.0 та недоступна у старих версіях.
Наслідком цього є те, що ви вже не можете використовувати боби з переглядом. Тепер вони будуть поводитись так, як квасоля. Один з недоліків полягає в тому, що вам потрібно самостійно відстежувати стан за допомогою прихованих входів та / або вільних параметрів запиту. В основному ці форми з полями введення з rendered
, readonly
або disabled
атрибутів , які контролюються АЯКС події будуть порушені.
Зауважте, що <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>
у наведеному вище прикладі.
Дивитися також
Не пов’язане з конкретною проблемою, використання 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" />
Дивитися також