【问题标题】:JSF without @ViewScoped没有 @ViewScoped 的 JSF
【发布时间】:2020-08-20 14:12:12
【问题描述】:

我已经使用 JSF 很多年了,在下一个项目中,我们的目标是使 Web 层尽可能无状态。我正在探索的一种可能性是删除@ViewScoped bean 以支持@RequestScoped(根据需要加上一两个@SessionScoped bean)。这对于具有 AJAX、数据表和条件渲染的复杂页面来说是很麻烦的。我的问题是:JSF(和 PrimeFaces)与无状态 Web bean 的工作情况如何?这是我应该继续探索的东西,还是@ViewScope 现在如此基础以至于不值得努力?

我很感激在我写这个问题时它可能会因为“主要基于意见”而被关闭,但我希望它不是,我对@ViewScope 解决的具体问题以及历史上的解决方法感兴趣我必须通过忽略@ViewScoped 来重新介绍。

【问题讨论】:

  • 听起来像旧的 jsf 1 次,我们将数据存储在隐藏字段中以恢复服务器上的状态(包括检查有效/操纵的隐藏字段)
  • “在下一个项目中,我们的目标是使 Web 层尽可能无状态。” 想要激励?我总是很好奇......我在一个项目中尝试过一次,它并没有超过它的小优势。当然不是在设置良好的服务器环境中。直截了当:纯 jsf 根本不应该有问题,对于 PrimeFaces 组件,它也应该可以工作,如果某个组件在某种情况下确实有问题,他们会很快解决这些问题(尽管数据表功能的复杂组合可能会被豁免)
  • 我的两分钱是这很难做到,特别是如果使用 Datatables。
  • 条件渲染对于隐藏的输入字段和/或请求参数来说“很容易”(尽管这样它们更容易被黑客篡改,所以你最好不要在敏感的东西上使用它们: ))。数据表确实很棘手,OptimusFaces 库通过自动更新请求查询字符串透明地使<p:dataTable> 的排序/过滤/分页功能完全幂等。这允许您使用请求范围的 bean 来支持它。这个周末我可能会写一个更详细的答案。
  • @Kukeltje 感谢您的评论。我们一直专注于通过对给定组件使用最窄的可行范围,使 Web 层尽可能无状态。然而,我们最近发现@RequestScope 现在几乎从未使用过,首选范围始终是@ViewScope。这导致业务逻辑基于便利性从服务层(通常是微服务)渗透到 Web 层。鉴于我们希望人们能够创建不仅仅是 JSF 客户端的客户端,因此提出了限制 @ViewScope 的想法。

标签: jsf primefaces view-scope


【解决方案1】:

JSF(和 PrimeFaces)与无状态 Web bean 的工作情况如何?

技术上可行。

JSF 主要使用视图状态来跟踪UIInputUICommand 组件的“禁用”、“只读”和“渲染”属性以及“提交值”、“本地值”和“已验证?” 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;
    }
}

注意:很遗憾,&lt;h:inputHidden&gt; 将无法工作,因为它仅在动作事件要排队之后更新模型值。即使上面没有immediate="true"顺便说一句,这让我想到了为 OmniFaces 建立一个新的&lt;o:inputHidden&gt;

通过这些更改,它会正常工作。

但是,由于最初是视图范围的状态(toggled 属性)现在已成为请求参数,因此它完全暴露给世界,因此也可以被黑客篡改。想要调用“提交”按钮而不首先调用“切换”按钮的黑客现在可以简单地手动添加请求参数toggled=true。这是否可取取决于您的应用程序的业务需求,但通常情况下它是完全不可取的。

这就是 JSF 试图通过提供将这些敏感属性放在 @ViewScoped bean 中的可能性来保护您免受攻击的原因。


这对于带有 AJAX、数据表和条件渲染的复杂页面来说很麻烦

没错,但在技术上仍然不是不可能的。您只需通过手动填充的隐藏输入字段手动执行分页、排序和过滤状态,如上所示。 &lt;p:dataTable&gt; 支持将这些状态绑定到 bean 属性。例如:

<p:dataTable ...
    first="#{bean.first}"
    sortField="#{bean.sortField}"
    sortOrder="#{bean.sortOrder}"
    filterBy="#{bean.filterBy}">
    ...
</p:dataTable> 

您可以像之前演示的那样将它们复制到&lt;input type="hidden"&gt; 字段中(确保被&lt;p:ajax update&gt; 覆盖!),最后通过@ManagedProperty 和/或@PostConstruct 获取它们。

实际上,您通过这种方式基本上重塑了当前由 javax.faces.ViewState 隐藏输入字段与 @ViewScoped bean 组合完成的工作。那么为什么不马上使用呢? :)

如果您主要关心的是内存使用情况,那么您需要仔细设计您的 bean,以便将视图范围状态存储在 @ViewScoped bean 中,并且 请求范围状态存储在@RequestScoped bean 中。例如,将数据模型放在请求范围 bean 中,将分页/排序/过滤状态放在视图范围 bean 中是非常好的。您可能还想考虑OmniFaces @ViewScoped,因为它会在页面卸载时立即破坏视图状态和物理 bean。

也就是说,考虑到这个问题,我已经 just a few hours ago 验证并改进了 OptimusFaces 库,以确保它也完全支持 &lt;f:view transient="true"&gt; 的无状态视图,以及新的集成测试。 OptimusFaces 的优点之一是您不再需要手动担心携带分页/排序/过滤状态。 OptimusFaces 会为您担心。

另见:

【讨论】:

  • 感谢您花时间提供详细的解释!本周我将调查 OmniFaces @ViewScope :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-14
  • 2012-09-29
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多