当与查询参数关联的转换/验证失败时执行重定向 [英] Performing a redirect, when conversion / validation associated with query parameters fails

查看:19
本文介绍了当与查询参数关联的转换/验证失败时执行重定向的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下是的一个简单用例.

The following is a simple use case of <f:viewAction>.

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

所涉及的托管 bean.

The managed bean involved.

@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 传递.当通过相关 URL 和与 < 的侦听器关联的 viewAction() 方法传递像 xxx 这样的非数字值时,会出现转换错误;f:viewAction> 未调用.

The parameter id is passed through a URL. There is a conversion error, when a non-numeric value like xxx is passed through the URL in question and the viewAction() method associated with the listener of <f:viewAction> is not invoked.

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

The value of id is null in this case. I would like to redirect to another page, when id is not convertible to a desired target type (like in this case) or id is not validated against the specified validation criteria to avoid potential exceptions which are likely to be thrown in the LazyDataModel#load() method of PrimeFaces or somewhere else in the associated managed bean whenever access to these parameters is attempted in the corresponding managed bean. For this to be so, the viewAction() method should be invoked.

如何处理?我应该使用

<f:event type="preRenderView">

结合?

推荐答案

这是指定行为.当 PROCESS_VALIDATIONS 阶段以 验证失败UPDATE_MODEL_VALUESINVOKE_APPLICATION 阶段都被跳过.就像在带有 的常规"表单中一样.把 想象成一个 和一个 作为一个 <h:commandButton> 会变得更清晰.

This is specified behavior. When PROCESS_VALIDATIONS phase ends with a validation failure, both the UPDATE_MODEL_VALUES and INVOKE_APPLICATION phases are skipped. Exactly like as in "regular" forms with <h:form>. Think of <f:viewParam> as a <h:inputText> and a <f:viewAction> as a <h:commandButton> and it will become more clear.

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

For your particular requirement, performing a redirect when conversion/validation has failed, there are at least 3 solutions:

  1. 如您所见,添加一个 .我宁愿挂钩 postValidate 事件,而不是为了更好的自我记录性.

  1. As you found out, add a <f:event listener>. I'd rather hook on postValidate event instead for better self-documentability.

<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() 的检查可防止在同一视图(如果有)中常规"表单验证失败时执行重定向.

The check on FacesContext#isPostback() prevents the redirect being performed on validation failures of "regular" forms in the same view (if any).

扩展内置的LongConvertergetAsObject() 中执行重定向(验证器不适合,因为 Long 的默认转换器在非数字输入上已经失败; 如果转换器失败,则永远不会触发验证器).然而,这是糟糕的设计(紧耦合).

Extend the builtin LongConverter whereby you perform the redirect in getAsObject() (a validator is insuitable as the default converter for Long would already fail on non-numeric inputs; if a converter fails, the validators are never fired). This is however poor design (tight-coupling).

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

}

如有必要,您可以在 中使用 来传递"参数给转换器.

You could if necessary play around with <f:attribute> inside <f:viewParam> to "pass" parameters to the converter.

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


  • 创建一个自定义标记处理程序,它的作用与 基本相同,但不需要额外的支持 bean 方法.


  • Create a custom taghandler which does basically the same as <f:event listener> but without the need for an additional backing bean method.

    <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 功能.

    True, it's a bit of code, but it ends up in clean and reusable <my:viewParamValidationFailed> tag and is actually a good fit for a new OmniFaces feature.

    这篇关于当与查询参数关联的转换/验证失败时执行重定向的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆