13

我正在使用ExternalContext.redirect(String); 将用户重定向到另一个页面的方法:

FacesContext.getCurrentInstance().addMessage(new FacesMessage("Bla bla bla..."));
FacesContext.getCurrentInstance().getExternalContext().getFlash().setKeepMessages(true);
ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
ec.redirect(ec.getRequestContextPath() + "/scenario.xhtml");

正如马特汉迪在他的回答中提到的那样,我使用了 Flash.setKeepMessages(true); 但它似乎不适用于 ExternalContext.redirect。(尽管当我通过从 bean 的操作方法返回页面名称进行重定向时它可以工作。)

现在如何添加 FacesMessage 以便它在重定向 (scenario.xhtml) 页面中可见?

4

3 回答 3

18

这似乎是一个时间问题。preRenderView在事件期间调用此侦听器方法。根据ELFlash(由返回的 Mojarra 的Flash实现ExternalContext#getFlash())的源代码,当您当前处于渲染响应阶段并且尚未为当前请求设置 flash cookie 时,它​​不会设置 flash cookie :

以下是来自的相关行ELFlash

if (currentPhase.getOrdinal() < PhaseId.RENDER_RESPONSE.getOrdinal()) {
    flashInfo = flashManager.getPreviousRequestFlashInfo();
} else {
    flashInfo = flashManager.getNextRequestFlashInfo(this, true);
    maybeWriteCookie(context, flashManager);
}

仅当maybeWriteCookieFlash cookie 需要第二次通过时(即,当重定向的页面又重定向到另一个页面时)才会设置 cookie。

这是一个不幸的极端案例。这个ELFlash逻辑是有道理的,但这不是你真正想要的。基本上,您需要在INVOKE_APPLICATION阶段添加消息。然而,没有这样的事件postInvokeAction。使用新的 JSF 2.2<f:viewAction>标记应该是可能的,因为它确实在调用应用程序阶段运行。

<f:viewAction action="#{bean.onload}" />

只要您还没有使用 JSF 2.2,您就需要寻找替代方法。最简单的方法是创建一个自定义ComponentSystemEvent.

@NamedEvent(shortName="postInvokeAction")
public class PostInvokeActionEvent extends ComponentSystemEvent {

    public PostInvokeActionEvent(UIComponent component) {
        super(component);
    }

}

现在你需要一个钩子来发布这个事件。最明智的地方是PhaseListener听完后阶段INVOKE_APPLICATION

public class PostInvokeActionListener implements PhaseListener {

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

    @Override
    public void beforePhase(PhaseEvent event) {
        // NOOP.
    }

    @Override
    public void afterPhase(PhaseEvent event) {
        FacesContext context = FacesContext.getCurrentInstance();
        context.getApplication().publishEvent(context, PostInvokeActionEvent.class, context.getViewRoot());
    }

}

如果您按以下方式注册它faces-config.xml

<lifecycle>
    <phase-listener>com.example.PostInvokeActionListener</phase-listener>
</lifecycle>

那么您将能够按如下方式使用新事件

<f:event type="postInvokeAction" listener="#{bean.onload}" />

更新这在 JSF 实用程序库OmniFaces中也可用,因此您不需要自制一个和另一个。另请参见InvokeActionEventListener展示示例

于 2012-06-04T20:26:44.720 回答
16

使用闪存通过重定向保留消息。

在重定向之前将这两行添加到您的代码中:

FacesContext context = FacesContext.getCurrentInstance();
context.getExternalContext().getFlash().setKeepMessages(true);

请注意,Mojarra 的闪存范围实现存在一些问题。如果您使用它,请记住这一点。

于 2012-05-15T07:17:47.113 回答
-2

使用 Matt Handy 的示例作为参考,我创建了以下对我非常有效的方法。

public static void Message(String message) {
    FacesMessage fm = new FacesMessage(FacesMessage.SEVERITY_INFO, mensagem, null);
    FacesContext context = FacesContext.getCurrentInstance();
    context.getExternalContext().getFlash().setKeepMessages(true);
    context.addMessage(null, fm);
}
于 2020-01-18T15:50:36.077 回答