JSF(和 PrimeFaces)与无状态 Web bean 的工作情况如何?
技术上可行。
JSF 主要使用视图状态来跟踪UIInput 和UICommand 组件的“禁用”、“只读”和“渲染”属性以及“提交值”、“本地值”和“已验证?” EditableValueHolder 组件的状态(由 UIInput 等实现)。
在“disabled”、“readonly”和“rendered”属性的情况下,如果它们代表一个EL表达式,那么JSF会在处理表单提交请求时重新检查它。下面是一个基本的例子:
<h:form>
<h:commandButton value="toggle" action="#{bean.toggle}">
<f:ajax render="panel" />
</h:commandButton>
<h:panelGroup id="panel">
<h:commandButton value="submit" action="#{bean.submit}" rendered="#{bean.toggled}">
<f:ajax />
</h:commandButton>
</h:panelGroup>
</h:form>
@Named
@ViewScoped
public class Bean implements Serializable {
private static final long serialVersionUID = 1L;
private boolean toggled;
public void toggle() {
this.toggled = !toggled;
}
public void submit() {
System.out.println("Submitted");
}
public boolean isToggled() {
return toggled;
}
}
首先单击“切换”按钮,然后单击“提交”按钮。如果是视图范围的 bean,它会工作得很好。但是,如果您在这里将@ViewScoped 替换为@RequestScoped,那么它将失败,因为toggled 默认回到false,此时JSF 需要在回发请求期间解码“提交”按钮,因此它的@ 987654339@ 属性将评估 false 并最终 JSF 不会排队操作事件。
在这种情况下,您需要确保在请求作用域 bean 的(后)构造期间将属性预初始化为预期值。一种方法是在 ajax-updated 组件中为此使用隐藏的输入字段。这是调整后的示例:
<h:form>
<h:commandButton value="toggle" action="#{bean.toggle}">
<f:ajax render="panel" />
</h:commandButton>
<h:panelGroup id="panel">
<input type="hidden" name="toggled" value="#{bean.toggled}" />
<h:commandButton value="submit" action="#{bean.submit}" rendered="#{bean.toggled}">
<f:ajax />
</h:commandButton>
</h:panelGroup>
</h:form>
@Named
@RequestScoped
public class Bean {
@Inject @ManagedProperty("#{param.toggled}")
private boolean toggled;
public void toggle() {
this.toggled = !toggled;
}
public void submit() {
System.out.println("Submitted");
}
public boolean isToggled() {
return toggled;
}
}
注意:很遗憾,<h:inputHidden> 将无法工作,因为它仅在动作事件要排队之后更新模型值。即使上面没有immediate="true"。 顺便说一句,这让我想到了为 OmniFaces 建立一个新的<o:inputHidden>。
通过这些更改,它会正常工作。
但是,由于最初是视图范围的状态(toggled 属性)现在已成为请求参数,因此它完全暴露给世界,因此也可以被黑客篡改。想要调用“提交”按钮而不首先调用“切换”按钮的黑客现在可以简单地手动添加请求参数toggled=true。这是否可取取决于您的应用程序的业务需求,但通常情况下它是完全不可取的。
这就是 JSF 试图通过提供将这些敏感属性放在 @ViewScoped bean 中的可能性来保护您免受攻击的原因。
这对于带有 AJAX、数据表和条件渲染的复杂页面来说很麻烦
没错,但在技术上仍然不是不可能的。您只需通过手动填充的隐藏输入字段手动执行分页、排序和过滤状态,如上所示。 <p:dataTable> 支持将这些状态绑定到 bean 属性。例如:
<p:dataTable ...
first="#{bean.first}"
sortField="#{bean.sortField}"
sortOrder="#{bean.sortOrder}"
filterBy="#{bean.filterBy}">
...
</p:dataTable>
您可以像之前演示的那样将它们复制到<input type="hidden"> 字段中(确保被<p:ajax update> 覆盖!),最后通过@ManagedProperty 和/或@PostConstruct 获取它们。
实际上,您通过这种方式基本上重塑了当前由 javax.faces.ViewState 隐藏输入字段与 @ViewScoped bean 组合完成的工作。那么为什么不马上使用呢? :)
如果您主要关心的是内存使用情况,那么您需要仔细设计您的 bean,以便仅将视图范围状态存储在 @ViewScoped bean 中,并且 仅 请求范围状态存储在@RequestScoped bean 中。例如,将数据模型放在请求范围 bean 中,将分页/排序/过滤状态放在视图范围 bean 中是非常好的。您可能还想考虑OmniFaces @ViewScoped,因为它会在页面卸载时立即破坏视图状态和物理 bean。
也就是说,考虑到这个问题,我已经 just a few hours ago 验证并改进了 OptimusFaces 库,以确保它也完全支持 <f:view transient="true"> 的无状态视图,以及新的集成测试。 OptimusFaces 的优点之一是您不再需要手动担心携带分页/排序/过滤状态。 OptimusFaces 会为您担心。
另见: