jsf - Performing a redirect, when conversion / validation associated with query parameters fails -
the following simple use case of <f:viewaction>
.
<f:metadata> <f:viewparam name="id" value="#{testmanagedbean.id}" maxlength="20"/> <f:viewaction action="#{testmanagedbean.viewaction}"/> </f:metadata>
the managed bean involved.
@managedbean @viewscoped public final class testmanagedbean implements serializable { private static final long serialversionuid = 1l; private long id; //getter , setter. public void viewaction() { system.out.println("viewaction() called : " + id); } }
the parameter id
passed through url. there conversion error, when non-numeric value xxx
passed through url in question , viewaction()
method associated listener of <f:viewaction>
not invoked.
the value of id
null
in case. redirect page, when id
not convertible desired target type (like in case) or id
not validated against specified validation criteria avoid potential exceptions thrown in lazydatamodel#load()
method of primefaces or somewhere else in associated managed bean whenever access these parameters attempted in corresponding managed bean. so, viewaction()
method should invoked.
how proceed this? should use
<f:event type="prerenderview">
in conjunction <f:viewaction>
?
this specified behavior. when process_validations
phase ends validation failure, both update_model_values
, invoke_application
phases skipped. in "regular" forms <h:form>
. think of <f:viewparam>
<h:inputtext>
, <f:viewaction>
<h:commandbutton>
, become more clear.
for particular requirement, performing redirect when conversion/validation has failed, there @ least 3 solutions:
as found out, add
<f:event listener>
. i'd rather hook onpostvalidate
event instead 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"); } }
the check on
facescontext#ispostback()
prevents redirect being performed on validation failures of "regular" forms in same view (if any).extend builtin
longconverter
whereby perform redirect ingetasobject()
(a validator insuitable default converterlong
fail on non-numeric inputs; if converter fails, validators never fired). 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 if necessary play around
<f:attribute>
inside<f:viewparam>
"pass" parameters 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);
create custom taghandler same
<f:event listener>
without need 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>
true, it's bit of code, ends in clean , reusable
<my:viewparamvalidationfailed>
tag , fit new omnifaces feature.
Comments
Post a Comment