JSF中@ViewScoped与@RequestScoped下Primefaces文件下载异常排查
问题根本原因
- @RequestScoped Bean的生命周期仅限单次HTTP请求:你首次通过带
contentFileId参数的GET请求打开页面时,ImageView Bean会正常初始化拿到参数值,页面渲染时按钮的<f:param>值是正确的。 - 点击下载按钮触发的是独立的POST回发请求,此时会重新实例化ImageView Bean,执行
@PostConstruct的init方法:这时候POST请求的地址是不带参数的http://localhost:8080/imageView.jsf,init方法从request里读不到contentFileId,所以字段值为null。 - 后续JSF解析
<f:param>的EL表达式#{imageView.contentFileId}时拿到的是null,导致StreamedContentView的getImage方法读取contentFileId为null,解析Long时抛出异常,触发JSF默认的错误回显逻辑,直接返回无参数的当前页面,不会触发下载。 - @ViewScoped Bean可以正常工作的原因是:Bean实例会保存在视图状态中,回发时不会重新实例化、也不会重新执行init方法,
contentFileId会保留首次GET请求拿到的有效值,所以参数传递正常。
解决方案
方案1:继续使用@RequestScoped,直接从请求参数取值
把<f:param>的取值改为直接读全局请求参数对象param,不依赖ImageView Bean的状态,修改成本最低:
<f:param name="contentFileId" value="#{param.contentFileId}" />
方案2:表单增加隐藏字段传递参数
在h:form内添加隐藏输入框,让参数随表单一起提交:
<h:form id="form"> <h:inputHidden value="#{imageView.contentFileId}"/> <!-- 原有按钮代码保持不变 --> </h:form>
回发时隐藏字段的值会作为POST参数提交,JSF会自动把值注入到新实例化的ImageView Bean的contentFileId字段中,后续<f:param>就能读到有效值。
方案3:保留@ViewScoped(更推荐)
如果没有特殊的作用域限制,当前场景本身就适合用@ViewScoped:它就是为同个视图下多次回发交互设计的,既能保留视图内的状态,又比SessionScoped节省资源,直接恢复@ViewScoped注解即可。
内容的提问来源于stack exchange,提问作者Pero122
相关产品推荐
相关产品推荐

