jsf - 当与查询参数关联的转换/验证失败时执行重定向

标签 jsf primefaces jsf-2.2

以下是<f:viewAction>的简单用例.

<f:metadata>
    <f:viewParam name="id" value="#{testManagedBean.id}" maxlength="20"/>
    <f:viewAction action="#{testManagedBean.viewAction}"/>
</f:metadata>

涉及的托管 bean。
@ManagedBean
@ViewScoped
public final class TestManagedBean implements Serializable {

    private static final long serialVersionUID = 1L;
    private Long id; //Getter and setter.

    public void viewAction() {
        System.out.println("viewAction() called : " + id);
    }
}

参数id通过 URL 传递。当非数字值如 xxx 时,会出现转换错误通过有问题的 URL 和 viewAction() 传递与 <f:viewAction> 的监听器关联的方法不被调用。
id的值是 null在这种情况下。我想重定向到另一个页面,当 id无法转换为所需的目标类型(如本例)或 id未针对指定的验证标准进行验证,以避免可能在 LazyDataModel#load() 中引发的潜在异常只要在相应的托管 bean 中尝试访问这些参数,PrimeFaces 或相关托管 bean 中的其他位置的方法。为此,viewAction()方法应该被调用。

如何进行?我应该使用
<f:event type="preRenderView">

结合 <f:viewAction> ?

最佳答案

这是 specified行为。当PROCESS_VALIDATIONS阶段以 validation failure 结束, 双方 UPDATE_MODEL_VALUESINVOKE_APPLICATION阶段被跳过。与 <h:form> 的“常规”形式完全一样.想想<f:viewParam>作为 <h:inputText>和一个 <f:viewAction>作为 <h:commandButton>并且会变得更加清晰。

对于您的特定要求,在转换/验证失败时执行重定向,至少有 3 种解决方案:

  • 如您所见,添加 <f:event listener> .我宁愿上钩 postValidate事件而不是更好的自我记录性。
    <f:metadata>
        <f:viewParam name="id" value="#{bean.id}" maxlength="20" />
        <f:event type="postValidate" listener="#{bean.redirectIfNecessary}" />
        <f:viewAction action="#{bean.viewAction}" />
    </f:metadata>
    

    public void redirectIfNecessary() throws IOException {
        FacesContext context = FacesContext.getCurrentInstance();
    
        if (!context.isPostback() && context.isValidationFailed()) {
            context.getExternalContext().redirect("some.xhtml");
        }
    }
    

    支票在 FacesContext#isPostback()防止在同一 View (如果有)中对“常规”表单的验证失败执行重定向。

  • 扩展内置 LongConverter 您可以在 getAsObject() 中执行重定向(验证器不适合,因为 Long 的默认转换器在非数字输入上已经失败;如果转换器失败,则永远不会触发验证器)。然而,这是糟糕的设计(紧耦合)。
    <f:metadata>
        <f:viewParam name="id" value="#{bean.id}" converter="idConverter" />
        <f:viewAction action="#{bean.viewAction}" />
    </f:metadata>
    

    @FacesConverter("idConverter")
    public class IdConverter extends LongConverter {
    
        @Override
        public Object getAsObject(FacesContext context, UIComponent component, String value) {
            if (value == null || !value.matches("[0-9]{1,20}")) {
                try {
                    context.getExternalContext().redirect("some.xhtml");
                    return null;
                }
                catch (IOException e) {
                    throw new FacesException(e);
                }
            }
            else {
                return super.getAsObject(context, component, value);
            }
        }
    
    }
    

    如有必要,您可以使用 <f:attribute><f:viewParam>将参数“传递”给转换器。
    <f:viewParam name="id" value="#{bean.id}" converter="idConverter">
        <f:attribute name="redirect" value="some.xhtml" />
    </f:viewParam>
    

    String redirect = (String) component.getAttributes().get("redirect");
    context.getExternalContext().redirect(redirect);
    

  • 创建一个与 <f:event listener> 基本相同的自定义标签处理程序但不需要额外的支持 bean 方法。
    <html ... xmlns:my="http://example.com/ui">
    
    <f:metadata>
        <f:viewParam name="id" value="#{bean.id}" maxlength="20" />
        <my:viewParamValidationFailed redirect="some.xhtml" />
        <f:viewAction action="#{bean.viewAction}" />
    </f:metadata>
    
    com.example.taghandler.ViewParamValidationFailed
    public class ViewParamValidationFailed extends TagHandler implements ComponentSystemEventListener {
    
        private String redirect;
    
        public ViewParamValidationFailed(TagConfig config) {
            super(config);
            redirect = getRequiredAttribute("redirect").getValue();
        }
    
        @Override
        public void apply(FaceletContext context, UIComponent parent) throws IOException {
            if (parent instanceof UIViewRoot && !context.getFacesContext().isPostback()) {
                ((UIViewRoot) parent).subscribeToEvent(PostValidateEvent.class, this);
            }
        }
    
        @Override
        public void processEvent(ComponentSystemEvent event) throws AbortProcessingException {
            FacesContext context = FacesContext.getCurrentInstance();
    
            if (context.isValidationFailed()) {
                try {
                    context.getExternalContext().redirect(redirect);
                }
                catch (IOException e) {
                    throw new AbortProcessingException(e);
                }
            }
        }
    
    }
    
    /WEB-INF/my.taglib.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <facelet-taglib
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
        version="2.0"
    >
        <namespace>http://example.com/ui</namespace>
    
        <tag>
            <tag-name>viewParamValidationFailed</tag-name>
            <handler-class>com.example.taghandler.ViewParamValidationFailed</handler-class>
        </tag>  
    </facelet-taglib>
    
    /WEB-INF/web.xml
    <context-param>
        <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
        <param-value>/WEB-INF/my.taglib.xml</param-value>
    </context-param>
    

    确实,这是一些代码,但它最终是干净且可重用的 <my:viewParamValidationFailed>标签,实际上非常适合新的 OmniFaces特征。
  • 关于jsf - 当与查询参数关联的转换/验证失败时执行重定向,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23049930/

    相关文章:

    jsf - JSF 直通输入元素的转换器 ("HTML5 friendly markup")

    java - 提交 JSF 页面时未调用 @AssertTrue 方法

    java - Scala Spring IoC 和 JSF

    jsf - 如何使用 <p :media>? 绑定(bind)动态内容

    java - 保留与使用 @idclass 的实体列表关联的实体

    jsf - 如何在Tomcat上安装和使用CDI?

    jsf - 动态创建 JSF 2.2 自定义组件

    java - 如何设置 J2EE 应用程序 (ear) 以在 weblogic 11g 中使用共享 JSF 和 JSTL 库?

    jsf - Primefaces 数据表行编辑器

    java - Primefaces 复选框客户端值