-
Available since OmniFaces 2.0
<o:viewParamValidationFailed>
allows the developer to handle a view parameter validation failure with either a redirect or an HTTP error status, optionally with respectively a flash message or HTTP error message. This tag can be placed inside <f:metadata>
or <f|o:viewParam>
. When placed in <f|o:viewParam>
, it will be applied when the particular view parameter has a validation error as per UIInput.isValid()
. When placed in <f:metadata>
, and no one view parameter has already handled the validation error via its own <o:viewParamValidationFailed>
, it will be applied when there's a general validation error as per FacesContext.isValidationFailed()
.
When the sendRedirect
attribute is set, a call to Faces.redirect(String, Object...)
is made internally to send the redirect. So, the same rules as to scheme and leading slash apply here. When the sendError
attribute is set, a call to Faces.responseSendError(int, String)
is made internally to send the error. You can therefore customize HTTP error pages via <error-page>
entries in web.xml
. Otherwise the server-default one will be displayed instead.
<f:viewParam required="true"> fail
As a precaution; be aware that <f:viewParam required="true">
has a design error in current Mojarra and MyFaces releases (as of now, Mojarra 2.2.7 and MyFaces 2.2.4). When the parameter is not specified in the query string, it is retrieved as null
, which causes an internal isRequired()
check to be performed instead of delegating the check to the standard UIInput
implementation. This has the consequence that PreValidateEvent
and PostValidateEvent
listeners are never invoked, which the <o:viewParamValidationFailed>
is actually relying on. This is fixed in <o:viewParam>
.
Examples
In the example below the client will be presented an HTTP 400 error when at least one view param is absent.
<f:metadata>
<o:viewParam name="foo" required="true" />
<o:viewParam name="bar" required="true" />
<o:viewParamValidationFailed sendError="400" />
</f:metadata>
In the example below the client will be redirected to "login.xhtml" when the "foo" parameter is absent, regardless of the "bar" parameter. When the "foo" parameter is present, but the "bar" parameter is absent, nothing new will happen. The process will proceed "as usual". I.e. the validation error will end up as a faces message in the current view the usual way.
<f:metadata>
<o:viewParam name="foo" required="true">
<o:viewParamValidationFailed sendRedirect="login.xhtml" />
</o:viewParam>
<o:viewParam name="bar" required="true" />
</f:metadata>
In the example below the client will be presented an HTTP 401 error when the "foo" parameter is absent, regardless of the "bar" or "baz" parameters. When the "foo" parameter is present, but either the "bar" or "baz" parameter is absent, the client will be redirected to "search.xhtml".
<f:metadata>
<o:viewParam name="foo" required="true">
<o:viewParamValidationFailed sendError="401" />
</o:viewParam>
<o:viewParam name="bar" required="true" />
<o:viewParam name="baz" required="true" />
<o:viewParamValidationFailed sendRedirect="search.xhtml" />
</f:metadata>
In a nutshell: when there are multiple <o:viewParamValidationFailed>
tags, they will be applied in the same order as they are declared in the view. So, with the example above, the one nested in <f|o:viewParam>
takes precedence over the one nested in <f:metadata>
.
Messaging
By default, the first occurring faces message on the parent component will be copied, or when there is none then the first occurring global faces message will be copied. When sendRedirect
is used, it will be set as a global flash error message. When sendError
is used, it will be set as HTTP status message.
You can override this message by explicitly specifying the message
attribute. This is applicable for both sendRedirect
and sendError
.
<o:viewParamValidationFailed sendRedirect="search.xhtml" message="You need to perform a search." />
...
<o:viewParamValidationFailed sendError="401" message="Authentication failed. You need to login." />
Note, although all of above examples use required="true"
, this does not mean that you can only use <o:viewParamValidationFailed>
in combination with required="true"
validation. You can use it in combination with any kind of conversion/validation on <f|o:viewParam>
, even bean validation.
Design notes
You can technically nest multiple <o:viewParamValidationFailed>
inside the same parent, but this is not the documented approach and only the first one would be used.
You can not change the HTTP status code of a redirect. This is not a Faces limitation, but an HTTP limitation. The status code of a redirect will always end up as the one of the redirected response. If you intend to "redirect" with a different HTTP status code, then you should be using sendError
instead and specify the desired page as <error-page>
in web.xml
.
This page has 6 view parameters: foo
,bar
, baz
, faz
,
boo
and foz
. All with a jakarta.faces.Long
converter, just for simplicity.
You can of course use your own converters and validators, e.g. required="true"
,
<f:validateLongRange>
, etcetera. In case conversion (or validation) fails, the associated
<o:viewParamValidationFailed>
logic will be executed.
The first four view parameters have each their own <o:viewParamValidationFailed>
,
demonstrating the following cases:
- Sending a HTTP 400 error with default validation message.
- Sending a HTTP 400 error with a custom message.
- Sending a redirect to
/whatsnew
with default validation message (via flash). - Sending a redirect to
/whatsnew
with a custom message (via flash).
The latter two view parameters which doesn't have their own <o:viewParamValidationFailed>
and are both making use of the global one sending a 400. The message is conditionally set based on whether it
was the foz
parameter which failed.
<f:metadata>
<f:viewParam name="foo" converter="jakarta.faces.Long">
<o:viewParamValidationFailed sendError="400" />
</f:viewParam>
<f:viewParam name="bar" converter="jakarta.faces.Long">
<o:viewParamValidationFailed sendError="400" message="Invalid bar!"/>
</f:viewParam>
<f:viewParam name="baz" converter="jakarta.faces.Long">
<o:viewParamValidationFailed sendRedirect="whatsnew" />
</f:viewParam>
<f:viewParam name="faz" converter="jakarta.faces.Long">
<o:viewParamValidationFailed sendRedirect="whatsnew" message="Invalid faz!" />
</f:viewParam>
<f:viewParam name="boo" converter="jakarta.faces.Long" />
<f:viewParam name="foz" converter="jakarta.faces.Long" />
<o:viewParamValidationFailed sendError="400" message="#{foz.valid ? null : 'Invalid foz!'}" />
</f:metadata>
<p>
This page has 6 view parameters: <code>foo</code>,<code>bar</code>, <code>baz</code>, <code>faz</code>,
<code>boo</code> and <code>foz</code>. All with a <code>jakarta.faces.Long</code> converter, just for simplicity.
You can of course use your own converters and validators, e.g. <code>required="true"</code>,
<code><f:validateLongRange></code>, etcetera. In case conversion (or validation) fails, the associated
<code><o:viewParamValidationFailed></code> logic will be executed.
</p>
<p>
The first four view parameters have each their own <code><o:viewParamValidationFailed></code>,
demonstrating the following cases:
</p>
<ul>
<li><a href="?foo=abc">Sending a HTTP 400 error with default validation message.</a></li>
<li><a href="?bar=abc">Sending a HTTP 400 error with a custom message.</a></li>
<li><a href="?baz=abc">Sending a redirect to <code>/whatsnew</code> with default validation message (via flash).</a></li>
<li><a href="?faz=abc">Sending a redirect to <code>/whatsnew</code> with a custom message (via flash).</a></li>
</ul>
<p>
The latter two view parameters which doesn't have their own <code><o:viewParamValidationFailed></code>
and are both making use of the global one sending a 400. The message is conditionally set based on whether it
was the <code>foz</code> parameter which failed.
</p>
<ul>
<li><a href="?boo=abc">Send a HTTP 400 error with default validation message.</a></li>
<li><a href="?foz=abc">Send a HTTP 400 error with a custom message.</a></li>
</ul>
<ui:composition template="/WEB-INF/templates/layout.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:ui="jakarta.faces.facelets"
xmlns:fn="jakarta.tags.functions"
xmlns:of="http://omnifaces.org/functions"
>
<ui:define name="contentTitle">Error</ui:define>
<ui:define name="content">
<ui:insert name="errorContent">
<ul>
<li>UUID: <strong>#{requestScope['org.omnifaces.exception_uuid']}</strong></li>
<li>Date/time: #{of:formatDate(now, 'yyyy-MM-dd HH:mm:ss')}</li>
<li>User agent: #{header['user-agent']}</li>
<li>User IP: #{empty header['x-forwarded-for'] ? request.remoteAddr : fn:split(header['x-forwarded-for'], ',')[0]}</li>
<li>Request URI: <a href="#{requestScope['jakarta.servlet.error.request_uri']}">#{requestScope['jakarta.servlet.error.request_uri']}</a></li>
<li>Ajax request: #{faces.ajaxRequest ? 'Yes' : 'No'}</li>
<li>Status code: #{requestScope['jakarta.servlet.error.status_code']}</li>
<li>Exception type: <code>#{requestScope['jakarta.servlet.error.exception_type']}</code></li>
<li>Exception message: <code>#{requestScope['jakarta.servlet.error.message']}</code></li>
<li>Stack trace: <pre><code>#{of:printStackTrace(requestScope['jakarta.servlet.error.exception'])}</code></pre></li>
</ul>
</ui:insert>
<h:outputScript rendered="#{faces.ajaxRequest}">scrollTo(0, 0);</h:outputScript>
</ui:define>
</ui:composition>
<ui:composition template="/WEB-INF/templates/errorpage.xhtml"
xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="jakarta.faces.html"
xmlns:ui="jakarta.faces.facelets"
>
<ui:define name="contentTitle">Wrong params?</ui:define>
<ui:define name="errorContent">
<p>
This was a bad request.
The server couldn't understand it.
Please verify if the request parameters are in the right format.
Alternatively, start at the left menu to find all available resources.
</p>
<ui:fragment rendered="#{not empty requestScope['jakarta.servlet.error.message']}">
<p>
The specific error message is: <code><h:outputText value="#{requestScope['jakarta.servlet.error.message']}" escape="false" /></code>
</p>
</ui:fragment>
</ui:define>
</ui:composition>