jsf 2.0 return http error code after failed GET parameter validation
I have this local URL: http://localhost:8084/Name/faces/Blah.xhtml?flkatid=AAA
and this is a segment from a facelet, that checks the parameter "flkatid" and assigns it to the bean:
<f:metadata>
<f:viewParam name="flkatid"
id="flkatid"
value="#{floKatBean.flkatid}"
required="true"
requiredMessage="URL incomplete"
validator="#{floKatBean.validateFlKatId}"
validatorMessage="URL incomplete or invalid"
>
</f:viewParam>
<f:event type="preRenderView" listener="#{floKatBean.init}"/>
<f:event type="preRenderView" listener="#{floBean.holFloListe}"/>
</f:metadata>
floKatBean.flkatid is an integer, thus the URL is invalid. A page is displayed, that tells this:
flkatid: 'AAA' must be a number consisting of one or more digits. flkatid: 'AAA' must be a number between -2147483648 and 2147483647 Example: 9346
JSF checks the parameter itself, because it knows that the bean element is an inte开发者_JAVA技巧ger. The custom validator is only called if the parameter is an integer.
Is it possible to get the server to return a HTTP error code, e.g. 403 (forbidden) without leaving the "standard JSF world" (or switch to RichFaces, MyFaces, SmileyFaces or whatever) ? I can't find anything in f:viewParam to "fail" the page. Maybe elsewhere?
You could do a ExternalContext#responseSendError()
inside the validator. But this requires that the value is been declared as a String
instead of Integer
and that required="true"
is been removed, all to prevent JSF default validation mechanism from doing the null/empty check and the conversion from String
to Integer
.
<f:metadata>
<f:viewParam name="flkatid" value="#{floKatBean.flkatid}"
validator="#{floKatBean.validateFlKatId}"
validatorMessage="URL incomplete or invalid"
/>
...
</f:metadata>
with
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException, IOException
{
if (value == null || !((String) value).matches("\\d+")) {
context.getExternalContext().responseSendError(
403, (String) component.getAttributes().get("validatorMessage"));
}
}
You could do the conversion in the init()
method instead.
this.flkatidInteger = Integer.valueOf(this.flatkid);
I've found that the best way to do this (dealing with request parameters and sending useful HTTP responses) is to drop the f:viewParam
s entirely, and deal "manually" with the params in a preRenderView
listener like this:
public void preRenderViewCheckParameters() throws IOException {
if (!isPostback()) {
flkatid = getFlkatidFromRequest();
if (flkatid == null) {
responseSendError(404, "URL incomplete or invalid!");
}
}
}
private Integer getFlkatidFromRequest(){
try {
return Integer.valueOf(getRequestParameter("flkatid"));
} catch (NumberFormatException e) {
return null;
}
}
// util methods stolen from OmniFaces (check out this project, you'll love it):
// https://github.com/omnifaces/omnifaces/blob/master/src/org/omnifaces/util/Faces.java
public static String getRequestParameter(String name) {
return FacesContext.getCurrentInstance().getExternalContext()
.getRequestParameterMap().get(name);
}
public static void responseSendError(int status, String message)
throws IOException {
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.getExternalContext().responseSendError(status, message);
facesContext.responseComplete();
}
and then, in the view:
<f:metadata>
<f:event listener="#{portariasMB.preRenderViewLoadPortaria}"
type="preRenderView" />
</f:metadata>
It's a little bit more code maybe, but it's more straightforward, at least.
Also, doing this way you avoid weird problems with messages not being displayed, so... that's what I've been doing.
精彩评论