javax.faces.application.ViewExpiredException: ไม่สามารถกู้คืนมุมมองได้


175

ฉันได้เขียนแอพพลิเคชั่นที่เรียบง่ายพร้อมความปลอดภัยที่จัดการโดยคอนเทนเนอร์ ปัญหาคือเมื่อฉันเข้าสู่ระบบและเปิดหน้าอื่นที่ฉันออกจากระบบแล้วฉันกลับมาที่หน้าแรกและฉันคลิกที่ลิงค์อื่น ๆ หรือหน้ารีเฟรชฉันได้รับข้อยกเว้นนี้ ฉันเดาว่าเป็นเรื่องปกติ (หรืออาจจะไม่ใช่ :)) เพราะฉันออกจากระบบและเซสชันถูกทำลาย ฉันควรทำอย่างไรเพื่อเปลี่ยนเส้นทางผู้ใช้ไปที่ตัวอย่างเช่น index.xhtml หรือ login.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)

คำตอบ:


353

บทนำ

ViewExpiredExceptionจะถูกโยนเมื่อjavax.faces.STATE_SAVING_METHODมีการตั้งค่าserver(เริ่มต้น) และ enduser จะส่งคำขอ HTTP POST ในมุมมองทาง<h:form>ด้วย<h:commandLink>, <h:commandButton>หรือ<f:ajax>ในขณะที่มุมมองของรัฐที่เกี่ยวข้องไม่สามารถใช้ได้ในเซสชั่นอีกต่อไป

สภาวะมุมมองที่ถูกระบุว่าเป็นค่าของช่องใส่ซ่อนของjavax.faces.ViewState <h:form>ด้วยการตั้งค่าวิธีการบันทึกสถานะนี้จะserverมีเพียง ID สถานะมุมมองที่อ้างอิงถึงสถานะมุมมองแบบอนุกรมในเซสชัน ดังนั้นเมื่อเซสชั่นหมดอายุด้วยเหตุผลบางอย่าง (หมดเวลาในฝั่งเซิร์ฟเวอร์หรือลูกค้าหรือคุกกี้เซสชั่นจะไม่รักษาอีกต่อไปด้วยเหตุผลบางอย่างในเบราว์เซอร์หรือโดยการโทรHttpSession#invalidate()ในเซิร์ฟเวอร์หรือเนื่องจากข้อผิดพลาดเฉพาะเซิร์ฟเวอร์กับคุกกี้เซสชัน รู้จักกันในWildFly ) จากนั้นสถานะการดูต่อเนื่องจะไม่สามารถใช้ได้อีกในเซสชันและ enduser จะได้รับข้อยกเว้นนี้ เพื่อให้เข้าใจถึงการทำงานของเซสชันดูที่servlets ทำงานอย่างไร instantiation เซสชันตัวแปรที่ใช้ร่วมกันและมัลติเธรด

นอกจากนี้ยังมีข้อ จำกัด เกี่ยวกับจำนวนการดูที่ JSF จะเก็บไว้ในเซสชัน เมื่อถึงขีด จำกัด แล้วการดูที่น้อยที่สุดที่ใช้ล่าสุดจะหมดอายุ ดูเพิ่มเติมcom.sun.faces.numberOfViewsInSession VS com.sun.faces.numberOfLogicalViews

กับการประหยัดวิธีการตั้งค่าไปยังรัฐclientที่javax.faces.ViewStateช่องป้อนซ่อนมีแทนทั้งสถานะของมุมมองต่อเนื่องดังนั้น enduser จะไม่ได้รับViewExpiredExceptionเมื่อช่วงหมดอายุ อย่างไรก็ตามยังสามารถเกิดขึ้นได้ในสภาพแวดล้อมแบบคลัสเตอร์ ("ข้อผิดพลาด: MAC ไม่ได้ตรวจสอบ" เป็นอาการ) และ / หรือเมื่อมีการหมดเวลาสำหรับการนำไปใช้งานเฉพาะในสถานะฝั่งไคลเอ็นต์ที่กำหนดค่าและ / หรือเมื่อเซิร์ฟเวอร์สร้างคีย์ AES ขึ้นใหม่ระหว่างการรีสตาร์ท ดูที่การรับ ViewExpiredException ในสภาพแวดล้อมแบบคลัสเตอร์ในขณะที่วิธีการบันทึกสถานะถูกตั้งค่าเป็นไคลเอนต์และเซสชันผู้ใช้ที่ถูกต้องวิธีการแก้ปัญหา

โดยไม่คำนึงถึงวิธีการแก้ปัญหาให้แน่ใจว่าคุณจะไม่ได้enableRestoreView11Compatibilityใช้ มันไม่ได้เลยคืนสถานะมุมมองเดิม โดยทั่วไปจะสร้างมุมมองใหม่และมุมมองที่เกี่ยวข้องทั้งหมดที่กำหนดขอบเขตจากจุดเริ่มต้นและด้วยเหตุนี้จึงสูญเสียข้อมูลดั้งเดิมทั้งหมด (สถานะ) เนื่องจากแอปพลิเคชันจะทำงานในลักษณะที่สับสน ("เฮ้ค่าการป้อนข้อมูลของฉันอยู่ที่ไหน .. ??") นี่เป็นสิ่งที่แย่มากสำหรับประสบการณ์ของผู้ใช้ ใช้มุมมองแบบไร้รัฐหรือดีกว่า<o:enableRestorableView>แทนเพื่อให้คุณสามารถจัดการได้ในมุมมองเฉพาะเท่านั้นแทนที่จะเป็นในทุกมุมมอง

สำหรับสาเหตุที่ JSF ต้องการบันทึกสถานะมุมมองให้ตอบคำถามนี้: เหตุใด JSF จึงบันทึกสถานะขององค์ประกอบ UI บนเซิร์ฟเวอร์

หลีกเลี่ยง 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 เพื่อส่งแบบฟอร์มโดยเฉพาะ หากคุณกำลังส่งแบบฟอร์มพร้อมกัน (ไม่ใช่อาแจ็กซ์) คุณควรกำหนดให้เป็นมุมมองแบบไร้รัฐ (ดูหัวข้อภายหลัง) หรือส่งการเปลี่ยนเส้นทางหลังจาก POST (ดูหัวข้อก่อนหน้า)

การViewExpiredExceptionรีเฟรชบนหน้าเป็นการกำหนดค่าเริ่มต้นเป็นกรณีที่หายากมาก มันสามารถเกิดขึ้นได้เมื่อมีการ จำกัด จำนวนการดู JSF ที่จะจัดเก็บในเซสชัน ดังนั้นจะเกิดขึ้นก็ต่อเมื่อคุณตั้งค่าขีด จำกัด นั้นต่ำเกินไปหรือว่าคุณกำลังสร้างมุมมองใหม่ใน "พื้นหลัง" อย่างต่อเนื่อง (เช่นโดยโพลอาแจ็กซ์ที่นำไปใช้อย่างไม่ดีในหน้าเดียวกัน หน้าข้อผิดพลาดเกี่ยวกับภาพที่แตกสลายในหน้าเดียวกัน) ดูเพิ่มเติมที่com.sun.faces.numberOfViewsInSession เทียบกับ com.sun.faces.numberOfLogicalViewsเพื่อดูรายละเอียดเกี่ยวกับขีด จำกัด นั้น อีกสาเหตุคือการมีไลบรารี JSF ที่ซ้ำกันใน classpath รันไทม์ที่ขัดแย้งกัน ขั้นตอนที่ถูกต้องในการติดตั้ง 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ในcontentหมายถึงจำนวนวินาทีก่อนที่จะเปลี่ยนเส้นทางซึ่ง0หมายความว่า "เปลี่ยนเส้นทางทันที" คุณสามารถใช้เช่น3เพื่อให้เบราว์เซอร์รอ 3 วินาทีด้วยการเปลี่ยนเส้นทาง)

ExceptionHandlerโปรดทราบว่าการจัดการข้อยกเว้นในระหว่างการร้องขออาแจ็กซ์ต้องพิเศษ ดูเพิ่มเติมหมดเวลาเซสชันและการจัดการ ViewExpiredException ใน JSF / PrimeFaces คำขออาแจ็กซ์ คุณสามารถค้นหาตัวอย่างสดได้ที่หน้าแสดงOmniFacesFullAjaxExceptionHandler (ซึ่งรวมถึงคำขอที่ไม่ใช่อาแจ็กซ์ )

นอกจากนี้ทราบว่า "ทั่วไป" หน้าข้อผิดพลาดของคุณควรจะแมปบน<error-code>ของ500แทน<exception-type>ของเช่นjava.lang.Exceptionหรือjava.lang.Throwableมิฉะนั้นข้อยกเว้นทั้งหมดห่อServletExceptionเช่นViewExpiredExceptionยังคงจบลงในหน้าข้อผิดพลาดทั่วไป ดูเพิ่มเติมViewExpiredException แสดงใน java.lang.Throwable ข้อผิดพลาดหน้าใน 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"ใน Mojarra (ยังไม่ได้ตรวจสอบ MyFaces ณ จุดนี้) โปรดทราบว่าฟีเจอร์นี้เปิดตัวใน Mojarra 2.1.19 และ 2.2.0 และไม่สามารถใช้ได้ในเวอร์ชั่นที่เก่ากว่า

ผลที่ตามมาคือคุณไม่สามารถใช้ถั่วที่ถูกกำหนดมุมมองได้อีกต่อไป ตอนนี้พวกเขาจะทำตัวเหมือนถั่วที่ถูกกำหนดขอบเขตการร้องขอ ข้อเสียอย่างหนึ่งคือคุณต้องติดตามสถานะด้วยตัวคุณเองโดยเล่นซอกับอินพุตที่ซ่อนอยู่และ / หรือพารามิเตอร์คำขอหลวม ส่วนใหญ่เป็นรูปแบบที่มีช่องใส่กับrendered, readonlyหรือdisabledแอตทริบิวต์ซึ่งถูกควบคุมโดยเหตุการณ์ที่อาแจ็กซ์จะได้รับผลกระทบ

โปรดทราบว่า<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>เพื่อให้เป็นแบบมีเงื่อนไข โปรดทราบว่ามันจะมีผลกับทั้งมุมมองไม่ใช่เฉพาะกับเนื้อหาที่ซ้อนอยู่เช่น<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" />

ดูสิ่งนี้ด้วย


ฉันจะทำอย่างไรกับการนำทางโดยนัยใน java ee 6 ฉันไม่ใช้ faces-config
l245c4l

1
โอ้คุณใช้ JSF 2.0 อยู่หรือเปล่า คุณควรพูดถึงเรื่องนี้ในคำถามของคุณ! เพิ่มไปยัง?faces-redirect=true outcomeฉันได้อัพเดตคำตอบแล้ว
BalusC

ใช่ฉันเพิ่งเริ่มต้นด้วย java ee :) และฉันกำลังใช้ faces-redirect = true ในการนำทางทั้งหมดของฉัน ฉันใช้ h: commandLink เฉพาะเมื่อมีการกระทำที่เกี่ยวข้องกับมัน ยกตัวอย่างเช่นการเชื่อมโยงออกจากระบบ ... ฉันมีการกระทำสตริงออกจากระบบ () ที่ฉันเซสชั่นโมฆะและเปลี่ยนเส้นทางการเข้าสู่ระบบ แต่ก็ไม่ได้ทำงานในหน้าเว็บที่ผมได้เข้าสู่ระบบและเป็นในขณะที่ออกจากระบบและพ่นที่ยกเว้น :(
l245c4l

1
ขอขอบคุณอีกครั้งและขอโทษสำหรับสิ่งนั้น :) แต่อย่างน้อยฉันก็ได้รับคำตอบที่รวดเร็วและเป็นมืออาชีพในเวลาไม่นาน: p
l245c4l

1
@LS: อย่างไรก็ตามตัวกรองยังคงมีผลบังคับใช้กับเคสเมื่อใดก็ตามที่มีการกดปุ่มย้อนกลับหลัง POST ที่หมดอายุและพยายามเรียกใช้คำขอ POST อื่นบนตัวกรอง สิ่งนี้จะส่งผลให้เกิดข้อยกเว้นนี้โดยไม่ตั้งใจ
BalusC

56

คุณลองเพิ่มบรรทัดด้านล่างลงในของคุณweb.xmlหรือไม่?

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

ฉันพบว่าสิ่งนี้มีประสิทธิภาพมากเมื่อฉันพบปัญหานี้


1
มันก็ใช้ได้สำหรับฉันเช่นกัน ขอบคุณสำหรับคำตอบ. จุดประสงค์ของสิ่งนี้คืออะไร?
MartK

2
จำไม่ผิด แต่ฉันพบโซลูชันนี้ในเว็บไซต์ ICEFaces
Mike GH

ฉันอาจจะไปงานปาร์ตี้ช้าไปหน่อย แต่สิ่งนี้ก็ใช้ได้กับฉันเช่นกัน ขอบคุณ!
stellarossa

4
สิ่งนี้กำหนดไว้สำหรับ JSF 1.2 หรือ JSF 2 ด้วยหรือไม่
SRy

17
สิ่งนี้จะหยุดโยนข้อยกเว้นเมื่อมุมมองหมดอายุและเพียงแค่ดำเนินการตามคำขอ แต่ JSF ยังคงไม่สามารถกู้คืนสถานะมุมมองหรือค้นหาถั่วที่ถูกกำหนดมุมมองที่เกี่ยวข้องใด ๆ ธุรกรรมนี้จะทำตัวเหมือนไร้สัญชาติ JSF และคุณจะต้องเรียกคืนสถานะมุมมองด้วยตนเองตามพารามิเตอร์คำขอ POST เพื่อหลีกเลี่ยง "wtf?" ประสบการณ์โดย enduser เมื่อประมวลผลแบบฟอร์มส่งการตอบกลับโดยไม่คาดคิด หากคุณต้องการใช้สิ่งนี้กับหน้า JSF เฉพาะเท่านั้นให้ใช้ OmniFaces <o:enableRestorableView>แทนพารามิเตอร์บริบทของแอปพลิเคชัน
BalusC

5

สิ่งแรกที่คุณต้องทำก่อนเปลี่ยนweb.xmlคือต้องแน่ใจว่า ManagedBean ของคุณimplements Serializable:

@ManagedBean
@ViewScoped
public class Login implements Serializable {
}

โดยเฉพาะอย่างยิ่งถ้าคุณใช้MyFaces


3

หลีกเลี่ยงรูปแบบหลายส่วนใน Richfaces:

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

หากคุณกำลังใช้ Richfaces ฉันพบว่า ajax ร้องขอภายในฟอร์มหลายส่วนส่งคืนรหัสมุมมองใหม่ในแต่ละคำขอ

วิธีแก้ปัญหา:

ในการร้องขอ ajax แต่ละครั้งจะส่งคืน View ID ซึ่งใช้ได้ตราบใดที่ View ID นั้นเหมือนกันเสมอ หากคุณได้รับ View ID ใหม่ในแต่ละคำขอแสดงว่ามีปัญหาและต้องแก้ไข


ระวังเมื่อเล่นกับโพลก็สามารถป้องกันเซสชันผู้ใช้ของคุณหมดอายุ ..
Jonathan Simas

0

คุณ coud ใช้ AjaxExceptionHandler หรือ Primefaces-Extensions ของคุณเอง

อัพเดต faces-config.xml ของคุณ

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

เพิ่มรหัสต่อไปนี้ในหน้า jsf ของคุณ

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

0

ฉันได้รับข้อผิดพลาดนี้: javax.faces.application.ViewExpiredException เมื่อฉันใช้การร้องขอที่แตกต่างกันฉันพบผู้ที่มี JsessionId เดียวกันแม้หลังจากรีสตาร์ทเซิร์ฟเวอร์ ดังนั้นนี่เป็นเพราะแคชของเบราว์เซอร์ เพียงปิดเบราว์เซอร์และลองใช้งาน


0

เมื่อหน้าของเราไม่ได้ใช้งานเป็นระยะเวลา x จำนวนการดูจะหมดอายุและส่ง javax.faces.application.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


วิธีนี้ไม่ได้เรียกคืนการกำหนดขอบเขตมุมมอง
BalusC

-2

โปรดเพิ่มบรรทัดนี้ใน web.xml ของคุณมันใช้งานได้สำหรับฉัน

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

4
คำตอบของคุณจะมีประโยชน์มากขึ้นถ้าคุณมีคำอธิบายและบริบทเกี่ยวกับสิ่งที่รหัสกำลังทำอยู่
Palpatim

1
แม้ว่านี่จะเป็นคำตอบที่ถูกต้อง StackOverflow ไม่สนับสนุนคำตอบเช่นนี้โดยไม่มีคำอธิบาย ชุมชนจะมีประโยชน์ในการเพิ่มข้อมูลเพิ่มเติมเกี่ยวกับสาเหตุที่ใช้งานได้
RacerNerd

-2

ฉันพบปัญหานี้ด้วยตัวเองและตระหนักว่าเป็นเพราะผลข้างเคียงของตัวกรองที่ฉันสร้างขึ้นซึ่งเป็นการกรองคำขอทั้งหมดในแอปพลิเคชัน ทันทีที่ฉันแก้ไขตัวกรองเพื่อเลือกเฉพาะคำขอบางอย่างปัญหานี้ก็ไม่เกิดขึ้น อาจเป็นการดีถ้าคุณตรวจสอบตัวกรองดังกล่าวในแอปพลิเคชันของคุณและดูว่ามันทำงานอย่างไร


-3

ฉันเพิ่มการกำหนดค่าต่อไปนี้ลงใน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>

6
คำแนะนำแย่มาก ทำเช่นนี้เฉพาะเมื่อคุณมีหน่วยความจำมากมายและผู้ใช้ปลายทางของคุณมีแท็บเบราว์เซอร์มากถึง 500 แท็บอย่างต่อเนื่องและกดปุ่มย้อนกลับของเบราว์เซอร์ได้ถึง 500 ครั้งเพื่อโพสต์ซิงโครนัสก่อนหน้า
BalusC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.