You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ADF中JSF页面检测浏览器关闭事件并调用后端Java方法求助

解决ADF Faces页面关闭时触发后端方法的问题

嘿,我明白你现在遇到的麻烦——想在用户关闭浏览器标签/窗口时触发后端Java方法,但ADF的serverListener没响应,同时还要给用户确认提示。咱们一步步拆解问题,搞定它!

先说说你原代码里的核心问题

  1. 事件绑定错误:你在af:document里用了onunload="onbeforeunload",这完全不对,应该绑定onbeforeunload事件,而不是onunload
  2. 异步请求被浏览器终止AdfCustomEvent.queue是异步操作,但现代浏览器在beforeunload事件中会快速终止大部分异步请求,还没等请求发到后端,页面就关了,所以serverListener根本收不到事件。
  3. 自定义提示文本失效:现在Chrome、Firefox等主流浏览器都不允许自定义beforeunload的提示文本了,只能显示浏览器默认的确认框,你设置的browser Is Closed !..不会显示出来。

解决方案:用可靠的方式触发后端请求 + 正确处理关闭提示

第一步:修改JS代码(BrowserEventClose.js)

navigator.sendBeacon这个专门为页面卸载场景设计的API,它能保证请求被后台处理,还不会阻塞页面关闭。同时正确触发浏览器的默认确认提示:

window.addEventListener('beforeunload', function(event) {
    // 触发浏览器默认的关闭确认提示(自定义文本已不被支持)
    event.preventDefault();
    event.returnValue = ''; // 非空值即可触发默认提示

    // 准备要发送给后端的参数
    const formData = new FormData();
    formData.append('testParam', 'so');

    // 发送请求到后端处理接口(替换成你的应用路径)
    navigator.sendBeacon('/your-app-context/close-handler', formData);
});

第二步:修改JSF页面

修正af:document的事件绑定,去掉无效的serverListener(因为我们用sendBeacon直接请求后端):

<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE html>
<f:view xmlns:f="http://java.sun.com/jsf/core" xmlns:af="http://xmlns.oracle.com/adf/faces/rich">
    <af:document title="testBrowserChrome.jsf" id="d1" clientComponent="true">
        <af:form id="f1">
            <af:resource type="javascript" source="resources/js/BrowserEventClose.js"/>
        </af:form>
    </af:document>
</f:view>

第三步:后端处理请求(创建Servlet)

写一个Servlet来接收sendBeacon的请求,然后调用你的ServerTest bean方法:

import javax.faces.context.FacesContext;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/close-handler")
public class CloseHandlerServlet extends HttpServlet {
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取前端传过来的参数
        String testParam = request.getParameter("testParam");
        
        // 获取你的ViewScope Bean并调用方法
        ServerTest serverTest = (ServerTest) FacesContext.getCurrentInstance()
                .getApplication()
                .evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{viewScope.ServerTest}", ServerTest.class);
        
        serverTest.testServerListener(testParam);
        
        // 返回成功状态
        response.setStatus(HttpServletResponse.SC_OK);
    }
}

如果你坚持要用ADF的ServerListener

如果不想用Servlet,也可以用同步AJAX请求(但会阻塞页面关闭,体验稍差),修改JS代码如下:

window.addEventListener('beforeunload', function(event) {
    event.preventDefault();
    event.returnValue = '';

    const docComponent = AdfPage.PAGE.findComponentByAbsoluteId('d1');
    if (docComponent) {
        // 构建ADF自定义事件的同步AJAX请求
        const request = new XMLHttpRequest();
        const clientId = docComponent.getClientId();
        // 同步请求(第三个参数为false)
        request.open('POST', window.location.pathname + '?javax.faces.partial.ajax=true', false);
        request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        
        // 组装ADF事件参数
        const params = `javax.faces.source=${clientId}&javax.faces.partial.execute=${clientId}&javax.faces.partial.render=${clientId}&javax.faces.event=test&test=so`;
        request.send(params);
    }
});

此时你的JSF页面可以保留af:serverListener

<af:serverListener type="test" method="#{viewScope.ServerTest.testServerListener}"/>

关键要点总结

  • beforeunload限制:现代浏览器不允许自定义提示文本,只能触发默认确认框;异步请求大概率会被终止,必须用navigator.sendBeacon或同步AJAX。
  • navigator.sendBeacon优势:异步、不阻塞页面关闭、浏览器保证请求送达,是页面卸载场景的最佳选择。
  • ADF事件的局限性:异步的AdfCustomEvent.queuebeforeunload里几乎无法生效,因为页面关闭速度远快于请求发送完成的速度。

内容的提问来源于stack exchange,提问作者mohamed essam

火山引擎 最新活动