Navigate to a different page in preRenderViewEvent depending on user permission; ExternalContext#dispatch() does not work

自古美人都是妖i 提交于 2019-12-29 08:13:31

问题


The page should be rendered only if user has permissions to see it. I try to achieve that by performing a check in PreRenderView event listener method:

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

And the method itself:

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();
        }
    }
}

When I try to look at denied page I see it, but get an exception in server log:

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:]
...

The cause: immediately after dispatch call, FacesContext.getCurrentInstance() returns null. But this don't interrupt current phase. So on the phase end PhaseListener is executed. It tries to get FacesContext instance, but gets null.

Can I avoid PhaseListener execution here? Or may be check for FacesContext current instance is not null should be added to omnifaces?

Thanks.


回答1:


Do not use ExternalContext#dispatch(), ever. This method has no single sensible use case in a decent JSF web application. It only corrupts the JSF life cycle because it creates another FacesContext within the current request. You should use JSF's own NavigationHandler or use ExternalContext#redirect().

When you're already on JSF 2.2, use <f:viewAction action="#{bean.init}"> which supports a navigation case outcome (like <h:commandButton action>):

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

Or when you're still on JSF 2.0/2.1, and can thus only use <f:event type="preRenderView">, and given that you're using OmniFaces use its Faces#navigate() or Faces#redirect():

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

In case you aren't using OmniFaces, here's how it's implemented:

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

Note that this all may still fail when using @PostConstruct instead of f:viewAction or preRenderView. See also a.o. Redirect in @PostConstruct causes IllegalStateException.



来源:https://stackoverflow.com/questions/29710680/navigate-to-a-different-page-in-prerenderviewevent-depending-on-user-permission

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!