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



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

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

涉及的托管豆。

@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 传递并且未调用与 <f:viewAction> 侦听器关联的 viewAction() 方法时,会出现转换错误。

在这种情况下,id的值null。当id无法转换为所需的目标类型(如本例中(或未根据指定的验证标准进行验证时id,我想重定向到另一个页面,以避免在 PrimeFaces 的LazyDataModel#load()方法或关联的托管 Bean 中的其他位置抛出的潜在异常,每当尝试在相应的托管 Bean 中访问这些参数时。为此,应调用 viewAction() 方法。

如何进行此操作?我应该使用

<f:event type="preRenderView">

结合<f:viewAction>

这是

指定的行为。当PROCESS_VALIDATIONS阶段以验证失败结束时,将跳过UPDATE_MODEL_VALUESINVOKE_APPLICATION阶段。就像在带有<h:form>的"常规"形式中一样.把<f:viewParam>想象成一个<h:inputText>,把<f:viewAction>想象成一个<h:commandButton>,它会变得更加清晰。

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

  1. 如您所发现的,请添加一个<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()的检查可防止对同一视图中"常规"表单(如果有(的验证失败执行重定向。


  2. 扩展内置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:viewParam>内部的<f:attribute>将参数"传递"到转换器。

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

  3. 创建一个自定义标记处理程序,其功能与<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 功能。

为什么不简单地验证自己id呢?

@ManagedBean
@ViewScoped
public final class TestManagedBean implements Serializable
{
    private String id;     //Getter and setter.
    private Long validId;  //Getter and setter.
    public void viewAction() {
        try {
            validId = Long.parseLong(id);
        } catch (NumberFormatException ex) {
            FacesContext facesContext = FacesContext.getCurrentInstance();
            String outcome = "redirect.xhtml";
            facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, outcome);
        }
    }
}

相关内容

  • 没有找到相关文章

最新更新