根据用户权限导航到preRenderViewEvent中的其他页面; ExternalContext#dispatch()不起作用

时间:2015-04-17 22:49:44

标签: jsf omnifaces

仅当用户有权查看该页面时,才应呈现该页面。我尝试通过在PreRenderView事件监听器方法中执行检查来实现这一点:

<f:event type="preRenderView" listener="#{theBean.init}" />

方法本身:

public void init() {
    if (user.havePermission()) {
        // backing bean init
    } else {
        FacesContext fc = FacesContext.getCurrentInstance();
        ExternalContext ec = fc.getExternalContext();
        ec.getRequestMap().put("message", no_permissions_message);
        try {
            ec.dispatch(ec.getRequestContextPath()
                + path_to_no_perm_page);
            fc.responseComlete();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

当我尝试查看被拒绝的页面时,我看到它,但在服务器日志中获得异常:

at org.omnifaces.util.FacesLocal.getRequestMap(FacesLocal.java:945) [omnifaces-1.7.jar:1.7]
at org.omnifaces.util.FacesLocal.getRequestAttribute(FacesLocal.java:953) [omnifaces-1.7.jar:1.7]
at org.omnifaces.util.Faces.getRequestAttribute(Faces.java:1416) [omnifaces-1.7.jar:1.7]
at org.omnifaces.eventlistener.CallbackPhaseListener.getCallbackPhaseListeners(CallbackPhaseListener.java:110) [omnifaces-1.7.jar:1.7]
at org.omnifaces.eventlistener.CallbackPhaseListener.afterPhase(CallbackPhaseListener.java:77) [omnifaces-1.7.jar:1.7]
at com.sun.faces.lifecycle.Phase.handleAfterPhase(Phase.java:189) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:107) [jsf-impl-2.1.7-jbossorg-2.jar:]
at com.sun.faces.lifecycle.LifecycleImpl.render(LifecycleImpl.java:139) [jsf-impl-2.1.7-jbossorg-2.jar:]
at javax.faces.webapp.FacesServlet.service(FacesServlet.java:594) [jboss-jsf-api_2.1_spec-2.0.1.Final.jar:2.0.1.Final]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
at org.primefaces.webapp.filter.FileUploadFilter.doFilter(FileUploadFilter.java:98) [primefaces-4.0.7.jar:4.0.7]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
at org.jboss.weld.servlet.ConversationPropagationFilter.doFilter(ConversationPropagationFilter.java:62) [weld-core-1.1.5.AS71.Final.jar:2012-02-10 15:31]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:280) [jbossweb-7.0.13.Final.jar:]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.13.Final.jar:]
...

原因:在调度调用之后,FacesContext.getCurrentInstance()立即返回null。但这不会中断当前阶段。所以在阶段结束时执行PhaseListener。它试图获取FacesContext实例,但是获取null。

我可以在这里避免执行PhaseListener吗? 或者可能是检查FacesContext当前实例是不是应该添加到omnifaces?

感谢。

1 个答案:

答案 0 :(得分:3)

永远不要使用ExternalContext#dispatch()。这个方法在一个不错的JSF Web应用程序中没有单一的合理用例。它只会破坏JSF生命周期,因为它会在当前请求中创建另一个FacesContext。您应该使用JSF自己的NavigationHandler或使用ExternalContext#redirect()

当您已经使用JSF 2.2时,请使用支持导航案例结果的<f:viewAction action="#{bean.init}">(例如<h:commandButton action>):

public String init() {
    // ...
    return "someViewId";
}

或者当你还在使用JSF 2.0 / 2.1时,只能使用<f:event type="preRenderView">,并且鉴于你使用的是OmniFaces,请使用Faces#navigate()Faces#redirect()

public void init() {
    // ...
    Faces.navigate("someViewId");
}

如果您不使用OmniFaces,请按照以下方式实施:

public void init() {
    // ...
    FacesContext context = FacesContext.getCurrentInstance();
    context.getApplication().getNavigationHandler().handleNavigation(context, null, "someViewId");
}

请注意,使用@PostConstruct代替f:viewActionpreRenderView时,这一切可能仍会失败。另见a.o. Redirect in @PostConstruct causes IllegalStateException