JSF 2 Facelets>在模板和页面中

发布于 2024-12-07 03:24:15 字数 6064 浏览 2 评论 0原文

我有以下模板 (masterLayout.xhtml):

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <f:view contentType="text/html">
        <ui:insert name="metadata"/>
        <h:head>
            <title><ui:insert name="windowTitle"/> | MySite</title>
        </h:head>

        <h:body>
            <div id="container">
                <div id="header">
                    <ui:insert name="header">
                        <ui:include src="/WEB-INF/templates/header.xhtml"/>
                    </ui:insert>
                </div>
                <div id="content">
                    <ui:insert name="content"/>
                </div>
                <div id="footer">
                    <ui:insert name="footer">
                        <ui:include src="/WEB-INF/templates/footer.xhtml"/>
                    </ui:insert>
                </div>
            </div>
        </h:body>
    </f:view>
</html>

和使用它的页面 (search.xhtml):

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title></title>
    </h:head>
    <h:body>
        <ui:composition template="/WEB-INF/templates/masterLayout.xhtml">
            <ui:define name="metadata">
                <f:metadata>
                    <f:viewParam name="address" value="#{searchBean.address}"/>
                    <f:event type="preRenderView" listener="#{userSessionBean.preRenderViewCookieLogin(e)}"/>
                    <f:event type="preRenderView" listener="#{searchBean.preRenderView(e)}"/>
                </f:metadata>
            </ui:define>
            <ui:define name="windowTitle">#{searchBean.address}</ui:define>

            <ui:define name="content">

                <!-- Content goes here -->

            </ui:define>
        </ui:composition>
    </h:body>
</html>

问题是我想在模板中调用 userSessionBean.preRenderViewCookieLogin(e) 因为还有很多其他页面。此方法检查用户是否已登录(根据会话状态),如果没有,则检查可用于登录用户的 cookie 是否可用,如果是(且有效),则自动使用户登录。系统在上面的代码中工作,但是当我尝试将其推送到模板中时,我的视图参数不再被设置。

这是上述内容的修改版本,将 userSessionBean.preRenderViewCookieLogin(e) 推送到模板。

masterLayout.xhtml:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <f:view contentType="text/html">
        <f:metadata>
            <f:event type="preRenderView" listener="#{userSessionBean.preRenderViewCookieLogin(e)}"/>
            <ui:insert name="metadata"/>
        </f:metadata>
        <h:head>
            <title><ui:insert name="windowTitle"/> | MySite</title>
        </h:head>

        <h:body>
            <div id="container">
                <div id="header">
                    <ui:insert name="header">
                        <ui:include src="/WEB-INF/templates/header.xhtml"/>
                    </ui:insert>
                </div>
                <div id="content">
                    <ui:insert name="content"/>
                </div>
                <div id="footer">
                    <ui:insert name="footer">
                        <ui:include src="/WEB-INF/templates/footer.xhtml"/>
                    </ui:insert>
                </div>
            </div>
        </h:body>
    </f:view>
</html>

search.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title></title>
    </h:head>
    <h:body>
        <ui:composition template="/WEB-INF/templates/masterLayout.xhtml">
            <ui:define name="metadata">
                <f:viewParam name="address" value="#{searchBean.address}"/>
                <f:event type="preRenderView" listener="#{searchBean.preRenderView(e)}"/>
            </ui:define>
            <ui:define name="windowTitle">#{searchBean.address}</ui:define>

            <ui:define name="content">

                <!-- Content goes here -->

            </ui:define>
        </ui:composition>
    </h:body>
</html>

请注意,我已将 标记移至模板。仅此一点就是问题所在,因为删除 userSessionBean.preRenderViewCookieLogin(e) 没有任何区别。我还尝试了一种有效的代码变体,即仅将 userSessionBean.preRenderViewCookieLogin(e) 移动到模板中,这意味着它不能位于 标签。在本例中,该方法在设置所有视图参数并调用 searchBean.preRenderView(e) 后执行。我希望在调用任何页面的 preRenderView(e) 之前而不是之后调用 userSessionBean.preRenderViewCookieLogin(e) 。只是为了好玩,我尝试在 userSessionBean.preRenderViewCookieLogin(e) 周围放置一个 ,它调用了此方法,但没有设置视图参数。

所以,我想知道:

  1. 为什么会发生这种情况,有没有办法解决?
  2. 有没有更好的方法来确保在执行其他操作之前为每个页面调用相同的方法?

编辑: 我只是尝试了其他东西 - 阶段事件: 这是在 masterLayout.xhtml 中。它根本没有被调用;不适用于任何阶段。

编辑: 删除了 e(该死的 NetBeans!): 这仅在渲染响应阶段之前调用,这当然意味着它在引发 preRenderView 事件之后调用。

I have the following template (masterLayout.xhtml):

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <f:view contentType="text/html">
        <ui:insert name="metadata"/>
        <h:head>
            <title><ui:insert name="windowTitle"/> | MySite</title>
        </h:head>

        <h:body>
            <div id="container">
                <div id="header">
                    <ui:insert name="header">
                        <ui:include src="/WEB-INF/templates/header.xhtml"/>
                    </ui:insert>
                </div>
                <div id="content">
                    <ui:insert name="content"/>
                </div>
                <div id="footer">
                    <ui:insert name="footer">
                        <ui:include src="/WEB-INF/templates/footer.xhtml"/>
                    </ui:insert>
                </div>
            </div>
        </h:body>
    </f:view>
</html>

and page that uses it (search.xhtml):

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title></title>
    </h:head>
    <h:body>
        <ui:composition template="/WEB-INF/templates/masterLayout.xhtml">
            <ui:define name="metadata">
                <f:metadata>
                    <f:viewParam name="address" value="#{searchBean.address}"/>
                    <f:event type="preRenderView" listener="#{userSessionBean.preRenderViewCookieLogin(e)}"/>
                    <f:event type="preRenderView" listener="#{searchBean.preRenderView(e)}"/>
                </f:metadata>
            </ui:define>
            <ui:define name="windowTitle">#{searchBean.address}</ui:define>

            <ui:define name="content">

                <!-- Content goes here -->

            </ui:define>
        </ui:composition>
    </h:body>
</html>

The problem is that I want to place the call to userSessionBean.preRenderViewCookieLogin(e) in the template because there are many other pages. This method checks that the user is logged in (according to session state) and, if not, checks that a cookie is available which can be used to log the user in and, if so (and if valid), logs the user in automatically. The system works in the code above, but when I try to push this into the template, my view parameters are no longer being set.

Here's the modified version of the above, with userSessionBean.preRenderViewCookieLogin(e) pushed up to the template.

masterLayout.xhtml:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets">
    <f:view contentType="text/html">
        <f:metadata>
            <f:event type="preRenderView" listener="#{userSessionBean.preRenderViewCookieLogin(e)}"/>
            <ui:insert name="metadata"/>
        </f:metadata>
        <h:head>
            <title><ui:insert name="windowTitle"/> | MySite</title>
        </h:head>

        <h:body>
            <div id="container">
                <div id="header">
                    <ui:insert name="header">
                        <ui:include src="/WEB-INF/templates/header.xhtml"/>
                    </ui:insert>
                </div>
                <div id="content">
                    <ui:insert name="content"/>
                </div>
                <div id="footer">
                    <ui:insert name="footer">
                        <ui:include src="/WEB-INF/templates/footer.xhtml"/>
                    </ui:insert>
                </div>
            </div>
        </h:body>
    </f:view>
</html>

search.xhtml

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:ui="http://java.sun.com/jsf/facelets"
      xmlns:f="http://java.sun.com/jsf/core">
    <h:head>
        <title></title>
    </h:head>
    <h:body>
        <ui:composition template="/WEB-INF/templates/masterLayout.xhtml">
            <ui:define name="metadata">
                <f:viewParam name="address" value="#{searchBean.address}"/>
                <f:event type="preRenderView" listener="#{searchBean.preRenderView(e)}"/>
            </ui:define>
            <ui:define name="windowTitle">#{searchBean.address}</ui:define>

            <ui:define name="content">

                <!-- Content goes here -->

            </ui:define>
        </ui:composition>
    </h:body>
</html>

Notice that I've moved the <f:metadata/> tag to the template. That alone is the problem as removing userSessionBean.preRenderViewCookieLogin(e) makes no difference. I also tried a variation on the code that worked, which was to just move userSessionBean.preRenderViewCookieLogin(e) into the template which means it can't be inside the <f:metadata/> tag. In this case, that method executed after all the view parameters had been set and searchBean.preRenderView(e) called. I want userSessionBean.preRenderViewCookieLogin(e) to be called before any page's preRenderView(e) is called, not after. And just for fun I tried putting an <f:metadata/> around userSessionBean.preRenderViewCookieLogin(e), which called this method, but didn't set the view parameters.

So, I would like to know:

  1. Why is this happening and is there a way to fix it?
  2. Is there a better way to ensure the same method is called for each page before anything else?

Edit:
I just tried something else - a phase event:
<f:view contentType="text/html" beforePhase="#{userSessionBean.beforePhase(e)}">
This is in masterLayout.xhtml. It is not being called at all; not for any phase.

Edit:
Removed the e (damn you NetBeans!):
<f:view contentType="text/html" beforePhase="#{userSessionBean.beforePhase}">
This is only called before the render response phase, which of course means it's called after the preRenderView event is raised.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

浊酒尽余欢 2024-12-14 03:24:15

为什么会发生这种情况?有办法解决吗?

来自 标签文档(第二段的重点是我的):

声明此视图的元数据方面。它必须是 的子级。 此标记必须驻留在给定 viewId 的顶级 XHTML 文件中,或者驻留在模板客户端中,但不能驻留在模板中。 实现必须确保构面的直接子级是 UIPanel,即使该方面只有一个子项。实现必须将 UIPanel 的 id 设置为 UIViewRoot.METADATA_FACET_NAME 符号常量的值。

因此,它确实必须放在顶视图中,而不是放在模板中。


是否有更好的方法来确保在执行其他操作之前为每个页面调用相同的方法?

在您的特定情况下,将登录用户存储为会话范围托管 Bean 的属性,而不是 Cookie并在适当的 URL 模式上使用 过滤器 进行检查。会话范围的托管 bean 在过滤器中可用作 HttpSession 属性。本地开发的 cookie 是不必要的,因为您基本上是在此处重新发明 HttpSession。除非您想要“记住我”功能,但这不应该以这种方式解决。也在过滤器中进行此操作。

另请参阅:

Why is this happening and is there a way to fix it?

From the <f:metadata> tag documentation (emphasis of 2nd paragraph is mine):

Declare the metadata facet for this view. This must be a child of the <f:view>. This tag must reside within the top level XHTML file for the given viewId, or in a template client, but not in a template. The implementation must insure that the direct child of the facet is a UIPanel, even if there is only one child of the facet. The implementation must set the id of the UIPanel to be the value of the UIViewRoot.METADATA_FACET_NAME symbolic constant.

So, it really has to go in the top view, not in the template.


Is there a better way to ensure the same method is called for each page before anything else?

In your particular case, store the logged-in user as a property of a session scoped managed bean instead of a cookie and use a filter on the appropriate URL pattern to check it. Session scoped managed beans are in the filter available as HttpSession attributes. Homegrown cookies are unnecessary as you're basically reinventing the HttpSession here. Unless when you want a "remember me" facility, but this should not be solved this way. Do it in a filter as well.

See also:

凉城 2024-12-14 03:24:15

我使用 PhaseListener 来完成这项工作。

public class CookieLoginPhaseListener implements PhaseListener
{
    @Override
    public void beforePhase(PhaseEvent event)
    {
    }

    @Override
    public void afterPhase(PhaseEvent event)
    {
        UserSessionBean userSessionBean = Util.lookupCdiBean("userSessionBean");
        userSessionBean.handleAuthCookie();
    }

    @Override
    public PhaseId getPhaseId()
    {
        return PhaseId.RESTORE_VIEW;
    }
}

一点点的bean查找魔法就在这里:

public static <T> T lookupCdiBean(String name)
{
    return (T) FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{" + name + "}", Object.class);
}

感谢@BalusC:JSF -按名称获取托管 bean。由于我使用的是 CDI,因此无法使用更具声明性的 @ManagedProperty 选项。不过没什么大不了的。

I got this working using a PhaseListener instead.

public class CookieLoginPhaseListener implements PhaseListener
{
    @Override
    public void beforePhase(PhaseEvent event)
    {
    }

    @Override
    public void afterPhase(PhaseEvent event)
    {
        UserSessionBean userSessionBean = Util.lookupCdiBean("userSessionBean");
        userSessionBean.handleAuthCookie();
    }

    @Override
    public PhaseId getPhaseId()
    {
        return PhaseId.RESTORE_VIEW;
    }
}

and the little bit of bean lookup magic is here:

public static <T> T lookupCdiBean(String name)
{
    return (T) FacesContext.getCurrentInstance().getApplication().evaluateExpressionGet(FacesContext.getCurrentInstance(), "#{" + name + "}", Object.class);
}

Thanks to @BalusC for this: JSF - get managed bean by name. Since I'm using CDI, I can't use the more declarative @ManagedProperty option. Not a big deal though.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文