以下是<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_VALUES
和INVOKE_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()
的检查可防止对同一视图中"常规"表单(如果有(的验证失败执行重定向。扩展内置
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);
创建一个自定义标记处理程序,其功能与
<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);
}
}
}