Primefaces OutputPanel延迟加载Ajax请求URL错误及排查方法咨询
遇到这种deferred加载组件的Ajax请求跑偏的情况,大概率是PrimeFaces的客户端脚本误选了页面上的纯HTML表单作为请求载体。下面一步步教你怎么验证问题根源,以及对应的解决思路:
一、先搞清楚Ajax请求的URL到底从哪来
1. 扒一扒渲染后的HTML代码
打开浏览器F12开发者工具,先找到那个p:outputPanel对应的DOM元素——因为是deferred加载,初始状态可能是个空的占位div,触发加载后才会渲染实际内容。你可以查看这个元素上的PrimeFaces相关属性,比如data-pf-deferred、data-url这类,看看里面的URL是不是已经错了。
同时找到页面上的纯HTML搜索表单,看看它的action属性是不是/search.jsf,再对比Ajax请求里带的search参数,是不是和这个表单里的输入框name对应上了——如果对应上,基本实锤是这个表单搞的鬼。
2. 追踪PrimeFaces的Ajax逻辑断点
去Sources面板里找到primefaces.js(或者压缩后的primefaces.min.js,可以开开发者工具的"格式化代码"功能),搜索DeferredRenderer或者getForm这类关键词。给PrimeFaces.ajax.AjaxUtils.getForm这个方法打个断点,然后触发deferred组件的加载(比如滚动到组件位置)。
当断点命中时,看看这个方法返回的form元素是哪个:正常应该是你的JSF表单(通常带j_idt开头的ID,或者你自定义的表单ID),如果返回的是那个纯HTML搜索表单,那就是问题所在——PrimeFaces错误地把非JSF表单当成了请求的载体。
3. 检查Ajax请求的Form Data
去Network面板里找到那个发错的Ajax请求,看Form Data里的内容:
- 如果有
search参数,说明PrimeFaces把纯HTML表单的字段也提交了; - 如果没有
javax.faces.ViewState这个JSF Ajax必带的参数,那肯定是没用到正确的JSF表单。
二、针对性解决问题
1. 明确指定Ajax要用的JSF表单
PrimeFaces默认会选页面上的第一个表单,要是你的纯HTML表单在JSF表单前面,就会被优先选中。解决办法很简单:
- 给你的JSF表单加个明确的ID,比如
<h:form id="productDetailForm">; - 然后在
p:outputPanel里通过deferredAjaxOpts指定要用的表单ID:
<p:outputPanel deferred="true"> <p:ajax event="load" deferredAjaxOpts="#{['form':'productDetailForm']}" /> <!-- 你的组件内容 --> </p:outputPanel>
这样就能强制PrimeFaces使用指定的JSF表单,不会再碰那个纯HTML表单。
2. 让PrimeFaces忽略纯HTML表单
要是不想调整表单顺序,也可以给纯HTML表单加个标记,让PrimeFaces跳过它。试试给纯HTML表单加个data-pf-ignore="true"属性:
<form action="/search.jsf" method="get" data-pf-ignore="true"> <!-- 搜索输入框 --> </form>
这个属性在PrimeFaces 7.x版本是支持的,能让组件的Ajax逻辑自动排除这个表单。
3. 考虑版本升级
你用的PrimeFaces 7.0.0是比较老的版本了,2019年的版本可能存在deferred组件处理非JSF表单的bug。去查一下PrimeFaces的release notes,7.0.x后续的小版本比如7.0.27有没有修复这类问题。如果项目允许,升级到更稳定的版本(比如8.x或者更高)会减少这类兼容性问题。
三、快速验证的小技巧
- 临时把那个纯HTML搜索表单注释掉,再测试deferred组件的Ajax请求——如果URL正常了,那100%是这个表单的问题;
- 多建几个带不同ID的JSF表单,分别指定给deferred组件,看看请求URL是不是跟着表单的action走,以此确认配置是否生效。
内容的提问来源于stack exchange,提问作者Sence

