【问题标题】:JSF 2.1 Redirect Preserving error messageJSF 2.1 重定向保留错误消息
【发布时间】:2012-08-14 15:34:13
【问题描述】:

我有以下 commandButton 操作方法处理程序:

public String reject()
{
  //Do something
  addMessage(null, "rejectAmountInvalid", FacesMessage.SEVERITY_ERROR);

  redirectToPortlet("/xxx/inbox?source=pendingActions#pendingApproval");
}

public static void addMessage(String clientId, String key, Severity level, Object... objArr)
{
    FacesContext context = FacesContext.getCurrentInstance();
    FacesMessage message = null;
    String msg = getTextFromResourceBundle(key);
    if (objArr != null && objArr.length > 0)
        msg = MessageFormat.format(msg, objArr);
    message = new FacesMessage(msg);

    message.setSeverity(level);
    context.addMessage(clientId, message);
}

public static void redirectToPortlet(String urlToRedirect)
{
    FacesContext context = FacesContext.getCurrentInstance();
    ExternalContext externalContext = context.getExternalContext();

    try
    {
        PortletRequest portletRequest = (PortletRequest) externalContext.getRequest();
        ThemeDisplay themeDisplay = (ThemeDisplay) portletRequest.getAttribute("THEME_DISPLAY");
        String portalURL = themeDisplay.getPortalURL();
        String redirect = portalURL + urlToRedirect;
        externalContext.redirect(redirect);
    }
    catch (Throwable e)
    {
        logger.log("Exception in redirectToPortlet to the URL: " + urlToRedirect, VLevel.ERROR, e);
    }
}

当页面重定向到“/xxx/inbox?source=pendingActions#pendingApproval”时,我添加的错误信息丢失了。有没有办法在 JSF 2.1 中保留错误消息?

谢谢 斯里

【问题讨论】:

  • 小提示,我宁愿用新消息重新抛出异常,也不愿在可能发生的每个小地方记录它们。如果函数声明禁止异常转发,您总是可以重新抛出 RuntimeException。这样,您只需注册一个全局异常处理程序即可记录所有消息。

标签: jsf


【解决方案1】:

您可以使用 PhaseListener 保存未显示的消息以供下一个请求使用。

我已经使用了一段时间,来自 Lincoln Baxter 的博客文章 Persist and pass FacesMessages over multiple page redirects,您只需将课程复制到某个包并在您的 faces-config.xml 上注册。

博文中没有明确提及,但我假设代码是公共领域的,所以我在这里发布一个更独立的答案:

package com.yoursite.jsf;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

/**
 * Enables messages to be rendered on different pages from which they were set.
 *
 * After each phase where messages may be added, this moves the messages
 * from the page-scoped FacesContext to the session-scoped session map.
 *
 * Before messages are rendered, this moves the messages from the
 * session-scoped session map back to the page-scoped FacesContext.
 *
 * Only global messages, not associated with a particular component, are
 * moved. Component messages cannot be rendered on pages other than the one on
 * which they were added.
 *
 * To enable multi-page messages support, add a <code>lifecycle</code> block to your
 * faces-config.xml file. That block should contain a single
 * <code>phase-listener</code> block containing the fully-qualified classname
 * of this file.
 *
 * @author Jesse Wilson jesse[AT]odel.on.ca
 * @secondaryAuthor Lincoln Baxter III lincoln[AT]ocpsoft.com 
 */
public class MultiPageMessagesSupport implements PhaseListener
{

    private static final long serialVersionUID = 1250469273857785274L;
    private static final String sessionToken = "MULTI_PAGE_MESSAGES_SUPPORT";

    public PhaseId getPhaseId()
    {
        return PhaseId.ANY_PHASE;
    }

    /*
     * Check to see if we are "naturally" in the RENDER_RESPONSE phase. If we
     * have arrived here and the response is already complete, then the page is
     * not going to show up: don't display messages yet.
     */
    // TODO: Blog this (MultiPageMessagesSupport)
    public void beforePhase(final PhaseEvent event)
    {
        FacesContext facesContext = event.getFacesContext();
        this.saveMessages(facesContext);

        if (PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            if (!facesContext.getResponseComplete())
            {
                this.restoreMessages(facesContext);
            }
        }
    }

    /*
     * Save messages into the session after every phase.
     */
    public void afterPhase(final PhaseEvent event)
    {
        if (!PhaseId.RENDER_RESPONSE.equals(event.getPhaseId()))
        {
            FacesContext facesContext = event.getFacesContext();
            this.saveMessages(facesContext);
        }
    }

    @SuppressWarnings("unchecked")
    private int saveMessages(final FacesContext facesContext)
    {
        List<FacesMessage> messages = new ArrayList<FacesMessage>();
        for (Iterator<FacesMessage> iter = facesContext.getMessages(null); iter.hasNext();)
        {
            messages.add(iter.next());
            iter.remove();
        }

        if (messages.size() == 0)
        {
            return 0;
        }

        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> existingMessages = (List<FacesMessage>) sessionMap.get(sessionToken);
        if (existingMessages != null)
        {
            existingMessages.addAll(messages);
        }
        else
        {
            sessionMap.put(sessionToken, messages);
        }
        return messages.size();
    }

    @SuppressWarnings("unchecked")
    private int restoreMessages(final FacesContext facesContext)
    {
        Map<String, Object> sessionMap = facesContext.getExternalContext().getSessionMap();
        List<FacesMessage> messages = (List<FacesMessage>) sessionMap.remove(sessionToken);

        if (messages == null)
        {
            return 0;
        }

        int restoredCount = messages.size();
        for (Object element : messages)
        {
            facesContext.addMessage(null, (FacesMessage) element);
        }
        return restoredCount;
    }
}

然后,在您的faces-config.xml

<phase-listener>com.yoursite.jsf.MultiPageMessagesSupport</phase-listener>

【讨论】:

    【解决方案2】:

    如果重定向到相同的路径,您可以使用Flash#setKeepMessages()

    context.getExternalContext().getFlash().setKeepMessages(true);
    

    通过这种方式,消息将持久保存在 flash 作用域中,只要单个后续 GET 请求(如在重定向期间发生),该作用域就会有效地存在。

    【讨论】:

    • 能否请您指出我应该在我提到的代码中添加上述行的确切位置?
    • 就在reject() 方法中。请再次注意,如果重定向到不同的路径,这将不起作用(由于当前 Mojarra 版本中仍未解决的错误)。使用 eljunior then 建议的 PhaseListener 方法。
    • 通过相同的路径,您的意思是在单击拒绝按钮之前最初加载页面时是“/xxx/inbox?source=pendingActions#pendingApproval”,那么我应该重定向到相同的路径“” /xxx/inbox?source=pendingActions#pendingApproval”。那么这个解决方案会起作用,对吗?
    • 基本上,重定向的资源应该与当前请求的资源在同一个“文件夹”中。请让我知道您是否尝试过?它开始听起来更像是你根本没有尝试过,这只会导致你为什么要问这一切的困惑。
    • 我正在尝试您的解决方案,但看起来它不在同一个文件夹中。所以我会尝试 Phaselistener 方法。
    猜你喜欢
    • 1970-01-01
    • 2012-09-24
    • 2020-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多