pengantar
The ViewExpiredException
akan dilempar setiap kali javax.faces.STATE_SAVING_METHOD
diatur ke server
(default) dan pengguna akhir mengirimkan permintaan HTTP POST pada tampilan melalui <h:form>
dengan <h:commandLink>
, <h:commandButton>
atau <f:ajax>
, sedangkan pandangan negara terkait tidak tersedia dalam sesi lagi.
Status tampilan diidentifikasi sebagai nilai bidang input tersembunyi javax.faces.ViewState
dari <h:form>
. Dengan metode penyimpanan keadaan diatur ke server
, ini hanya berisi ID negara tampilan yang mereferensikan keadaan tampilan serial dalam sesi. Jadi, ketika sesi kadaluarsa karena alasan tertentu (baik habis di server atau di sisi klien, atau cookie sesi tidak dipertahankan lagi karena beberapa alasan di browser, atau dengan memanggil HttpSession#invalidate()
server, atau karena bug khusus server dengan cookie sesi sebagai dikenal di WildFly ), maka keadaan tampilan serial tidak tersedia lagi di sesi dan pengguna akhir akan mendapatkan pengecualian ini. Untuk memahami cara kerja sesi, lihat juga Bagaimana servlets bekerja? Instansiasi, sesi, variabel bersama, dan multithreading .
Ada juga batasan jumlah tampilan yang akan disimpan JSF dalam sesi tersebut. Ketika batas tercapai, maka tampilan yang paling terakhir digunakan akan kedaluwarsa. Lihat juga com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews .
Dengan metode penyimpanan keadaan disetel ke client
, javax.faces.ViewState
bidang input tersembunyi alih-alih berisi seluruh kondisi tampilan serial, jadi pengguna akhir tidak akan mendapatkan ViewExpiredException
ketika sesi berakhir. Namun itu masih dapat terjadi pada lingkungan cluster ("ERROR: MAC tidak memverifikasi" adalah gejala) dan / atau ketika ada batas waktu implementasi khusus pada keadaan sisi klien yang dikonfigurasi dan / atau ketika server menghasilkan kembali kunci AES selama restart , lihat juga Mendapatkan ViewExpiredException di lingkungan berkerumun sementara metode penyimpanan keadaan diatur ke klien dan sesi pengguna valid bagaimana menyelesaikannya.
Terlepas dari solusinya, pastikan Anda tidak menggunakan enableRestoreView11Compatibility
. itu sama sekali tidak mengembalikan keadaan tampilan asli. Ini pada dasarnya menciptakan kembali pandangan dan semua kacang scoped terkait tampilan dari awal dan dengan demikian kehilangan semua data asli (negara). Karena aplikasi akan berperilaku membingungkan ("Hei, di mana nilai input saya .. ??"), ini sangat buruk untuk pengalaman pengguna. Lebih baik gunakan tampilan stateless atau <o:enableRestorableView>
sebagai gantinya sehingga Anda bisa mengelolanya hanya pada tampilan tertentu alih-alih pada semua tampilan.
Mengenai mengapa JSF perlu menyimpan status tampilan, kunjungi jawaban ini: Mengapa JSF menyimpan status komponen UI di server?
Menghindari ViewExpiredException pada navigasi halaman
Untuk menghindari ViewExpiredException
ketika misalnya menavigasi kembali setelah logout ketika penyimpanan negara diatur ke server
, hanya mengarahkan permintaan POST setelah logout tidak cukup. Anda juga perlu menginstruksikan browser untuk tidak melakukan cache halaman JSF dinamis, jika tidak browser dapat menunjukkannya dari cache alih-alih meminta yang baru dari server ketika Anda mengirim permintaan GET di atasnya (misalnya dengan tombol kembali).
Bidang javax.faces.ViewState
tersembunyi dari halaman yang di-cache dapat berisi nilai ID status tampilan yang tidak berlaku lagi di sesi saat ini. Jika Anda (ab) menggunakan POST (tautan / tombol perintah) alih-alih GET (tautan / tombol biasa) untuk navigasi halaman-ke-halaman, dan klik tautan / tombol perintah seperti itu pada halaman cache, maka ini pada gilirannya akan gagal dengan a ViewExpiredException
.
Untuk mengaktifkan redirect setelah logout di JSF 2.0, tambahkan <redirect />
ke <navigation-case>
pertanyaan yang dimaksud (jika ada), atau tambahkan ?faces-redirect=true
ke outcome
nilainya.
<h:commandButton value="Logout" action="logout?faces-redirect=true" />
atau
public String logout() {
// ...
return "index?faces-redirect=true";
}
Untuk menginstruksikan browser untuk tidak men-cache halaman JSF dinamis, buat Filter
yang dipetakan pada nama servlet FacesServlet
dan menambahkan header respons yang diperlukan untuk menonaktifkan cache browser. Misalnya
@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);
}
// ...
}
Menghindari ViewExpiredException pada penyegaran halaman
Untuk menghindari ViewExpiredException
saat me-refresh halaman saat ini ketika tabungan negara diatur ke server
, Anda tidak hanya perlu memastikan Anda melakukan navigasi halaman-ke-halaman secara eksklusif oleh GET (tautan / tombol biasa), tetapi Anda juga harus memastikan Anda secara eksklusif menggunakan ajax untuk mengirimkan formulir. Jika Anda mengirimkan form secara sinkron (non-ajax), maka sebaiknya Anda membuat tampilan stateless (lihat bagian selanjutnya), atau untuk mengirim redirect setelah POST (lihat bagian sebelumnya).
Memiliki ViewExpiredException
refresh halaman pada konfigurasi default adalah kasus yang sangat langka. Itu hanya bisa terjadi ketika batas jumlah tampilan yang akan disimpan JSF di sesi itu dipukul. Jadi, itu hanya akan terjadi ketika Anda menetapkan batas itu terlalu rendah secara manual, atau bahwa Anda terus-menerus membuat tampilan baru di "latar belakang" (misalnya oleh jajak pendapat ajax yang diterapkan secara buruk di halaman yang sama atau oleh 404 yang diterapkan dengan buruk halaman kesalahan pada gambar yang rusak dari halaman yang sama). Lihat juga com.sun.faces.numberOfViewsInSession vs com.sun.faces.numberOfLogicalViews untuk detail tentang batas itu. Penyebab lain adalah memiliki duplikat pustaka JSF di runtime classpath yang saling bertentangan. Prosedur yang benar untuk menginstal JSF diuraikan dalam halaman wiki JSF kami .
Menangani ViewExpiredException
Saat Anda ingin menangani hal yang tidak terhindarkan ViewExpiredException
setelah tindakan POST pada halaman arbitrer yang sudah dibuka di beberapa tab / jendela peramban saat Anda keluar di tab / jendela lain, maka Anda ingin menentukan error-page
untuk apa web.xml
yang terjadi ke halaman "Sesi Anda kehabisan waktu". Misalnya
<error-page>
<exception-type>javax.faces.application.ViewExpiredException</exception-type>
<location>/WEB-INF/errorpages/expired.xhtml</location>
</error-page>
Gunakan jika perlu tajuk meta refresh di halaman kesalahan jika Anda berniat untuk benar-benar mengarahkan lebih jauh ke halaman beranda atau masuk.
<!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
dalam content
mewakili jumlah detik sebelum redirect, 0
dengan demikian berarti "segera redirect", Anda dapat menggunakan misalnya 3
untuk membiarkan browser menunggu 3 detik dengan redirect)
Perhatikan bahwa penanganan pengecualian selama permintaan ajax memerlukan khusus ExceptionHandler
. Lihat juga Sesi waktu habis dan penanganan ViewExpiredException pada permintaan JSF / PrimeFaces ajax . Anda dapat menemukan contoh langsung di halaman showcase OmniFacesFullAjaxExceptionHandler
(ini juga mencakup permintaan non-ajax).
Juga mencatat bahwa "umum" halaman kesalahan Anda harus dipetakan pada <error-code>
dari 500
bukan sebuah <exception-type>
dari misalnya java.lang.Exception
atau java.lang.Throwable
, kalau tidak semua pengecualian dibungkus ServletException
seperti ViewExpiredException
masih akan berakhir di halaman kesalahan umum. Lihat juga ViewExpiredException yang diperlihatkan di java.lang.Halaman kesalahan yang bisa dipecahkan di web.xml .
<error-page>
<error-code>500</error-code>
<location>/WEB-INF/errorpages/general.xhtml</location>
</error-page>
Tampilan tanpa kewarganegaraan
Alternatif yang sama sekali berbeda adalah menjalankan tampilan JSF dalam mode stateless. Dengan cara ini, tidak ada status JSF yang akan disimpan dan tampilan tidak akan pernah kedaluwarsa, tetapi dibangun kembali dari awal pada setiap permintaan. Anda dapat mengaktifkan tampilan stateless dengan mengatur transient
atribut <f:view>
untuk true
:
<f:view transient="true">
</f:view>
Dengan cara ini javax.faces.ViewState
bidang tersembunyi akan mendapatkan nilai tetap "stateless"
di Mojarra (belum memeriksa MyFaces saat ini). Perhatikan bahwa fitur ini diperkenalkan di Mojarra 2.1.19 dan 2.2.0 dan tidak tersedia dalam versi yang lebih lama.
Konsekuensinya adalah Anda tidak bisa lagi menggunakan view scoped bean. Mereka sekarang akan berperilaku seperti permintaan kacang scoped. Salah satu kelemahannya adalah Anda harus melacak status Anda sendiri dengan mengutak-atik input tersembunyi dan / atau parameter permintaan yang longgar. Terutama formulir-formulir dengan bidang input dengan rendered
, readonly
atau disabled
atribut yang dikendalikan oleh peristiwa ajax akan terpengaruh.
Perhatikan bahwa <f:view>
tidak harus unik di seluruh tampilan dan / atau berada di templat induk saja. Ini juga sepenuhnya sah untuk mendeklarasikan ulang dan menaruhnya di klien template. Itu pada dasarnya "memperpanjang" orang tua <f:view>
itu. Misalnya dalam templat induk:
<f:view contentType="text/html">
<ui:insert name="content" />
</f:view>
dan di klien template:
<ui:define name="content">
<f:view transient="true">
<h:form>...</h:form>
</f:view>
</f:view>
Anda bahkan dapat membungkus <f:view>
dalam <c:if>
untuk membuatnya bersyarat. Perhatikan bahwa itu akan berlaku pada seluruh tampilan, tidak hanya pada konten yang bersarang, seperti <h:form>
pada contoh di atas.
Lihat juga
Tidak terkait dengan masalah nyata, menggunakan HTTP POST untuk navigasi murni halaman-ke-halaman tidak terlalu ramah pengguna / SEO. Di JSF 2.0 Anda harus benar-benar lebih suka <h:link>
atau <h:button>
lebih dari <h:commandXxx>
yang untuk navigasi halaman-ke-halaman vanila biasa.
Jadi, alih-alih misalnya
<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>
lebih baik lakukan
<h:link value="Foo" outcome="foo" />
<h:link value="Bar" outcome="bar" />
<h:link value="Baz" outcome="baz" />
Lihat juga