验证错误后jsf viewparam丢失 [英] jsf viewparam lost after validation error

查看:77
本文介绍了验证错误后jsf viewparam丢失的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我面临以下问题:在一页中,我列出了应用程序的所有用户,并且每个用户都有一个编辑"按钮,这是带有?id=<userid>的"GET"链接.

编辑页面的元数据中有<f:viewParam name="id" value="#{editUserBean.id}"/>.
如果我犯了一些输入错误并提交(我使用CDI Weld Bean验证),则会再次显示该页面,但是我丢失了URL中的?id=...,因此丢失了我正在编辑的用户的用户ID. /p>

我查看了 JSF验证错误,价值丢失中描述的类似问题,但输入隐藏的解决方案(或更糟糕的是,使用战斧"(看上去过于杀伤力))需要大量的代码.

我尝试用CDI添加一个会话",它可以正常工作,但对我来说又显得过于矫kill过头.

JSF中是否存在一种简单的解决方案,以在发生验证错误时保留视图参数?

[我的环境:Tomcat7 + MyFaces 2.1.0 + Hibernate Validator 4.2.0 + CDI(Weld)1.1.2]

解决方案

有趣的情况.对于每个人来说,以下最少的代码都可以重现这一点:

手镯:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
>

    <f:metadata>
        <f:viewParam id="id" name="id" value="#{viewParamBean.id}"/>
    </f:metadata>

    <h:body>

        <h:messages />

        #{viewParamBean.id} <br/>

        <h:form>
            <h:inputText value="#{viewParamBean.text}" >
                <f:validateLength minimum="2"/>
            </h:inputText>

            <h:commandButton value="test" action="#{viewParamBean.actionMethod}"/>
        </h:form>

    </h:body>
</html>

Bean:

@ManagedBean
@RequestScoped
public class ViewParamBean {

    private long id;    
    private String text;

    public void actionMethod() {

    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }    
}

如果使用viewparam.xhtml?id=12调用Facelet,它将在屏幕上显示12.如果您随后输入了有效的内容,例如aaaaa,该ID将从URL中消失,但会一直显示在屏幕上(归因于ui组件的有状态性).

但是...如OP所述,一旦发生任何验证器错误(例如,输入a),该ID将永久丢失.此后输入有效输入将不会将其取回.几乎看起来像是个错误,但是我尝试了Mojarra 2.1和Myfaces 2.1,它们的行为相同.

更新:

经过检查,问题似乎出在"UIViewParameter"(Mojarra)的这种方法中:

public void encodeAll(FacesContext context) throws IOException {
    if (context == null) {
        throw new NullPointerException();
    }

    // if there is a value expression, update view parameter w/ latest value after render
    // QUESTION is it okay that a null string value may be suppressing the view parameter value?
    // ANSWER: I'm not sure.
    setSubmittedValue(getStringValue(context));
}

然后更具体地讲这种方法:

public String getStringValue(FacesContext context) {
    String result = null;
    if (hasValueExpression()) {
        result = getStringValueFromModel(context);
    } else {
        result = (null != rawValue) ? rawValue : (String) getValue();
    }
    return result;
}

因为hasValueExpression()为true,它将尝试从模型(后备bean)中获取值.但是由于此bean是请求范围的,因此此请求将没有任何值,因为验证刚刚失败,因此从未设置任何值.实际上,UIViewParameter的有状态值会被后备Bean作为默认值返回的任何值覆盖(通常为null,但这当然取决于您的Bean).

一种解决方法是制作bean @ViewScoped,无论如何它通常是一个更好的范围(我假设您使用该参数从Service获取用户,并且可能不必在每次回发时都一遍又一遍地进行操作).

另一种替代方法是创建自己的UIViewParameter版本,如果验证失败(基本上所有其他UIInput组件都这样做),则不会尝试从模型中获取值.

I'm facing the following issue: in one page, I list all users of my application and have an "edit" button for each one, which is a "GET" link with ?id=<userid>.

The edit page has a <f:viewParam name="id" value="#{editUserBean.id}"/> in metadata.
If I made some input mistakes and submit (I use CDI Weld Bean validation), the page is displayed again, but I've lost the ?id=... in the URL and so lose the user id of the user I'm editing.

I've looked at a similar problem described in JSF validation error, lost value, but the solution with inputhidden (or worse, with tomahawk, which looks overkill) requires lot of uggly code.

I've tried adding a "Conversation" with CDI, and it is working, but it looks like too much overkill to me again.

Does there exists a simple solution in JSF to preserve view parameters in case of validation errors?

[My environment: Tomcat7 + MyFaces 2.1.0 + Hibernate Validator 4.2.0 + CDI(Weld) 1.1.2]

解决方案

Interesting case. For everyone, the following minimal code reproduces this:

Facelet:

<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core"
>

    <f:metadata>
        <f:viewParam id="id" name="id" value="#{viewParamBean.id}"/>
    </f:metadata>

    <h:body>

        <h:messages />

        #{viewParamBean.id} <br/>

        <h:form>
            <h:inputText value="#{viewParamBean.text}" >
                <f:validateLength minimum="2"/>
            </h:inputText>

            <h:commandButton value="test" action="#{viewParamBean.actionMethod}"/>
        </h:form>

    </h:body>
</html>

Bean:

@ManagedBean
@RequestScoped
public class ViewParamBean {

    private long id;    
    private String text;

    public void actionMethod() {

    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }    
}

If you call the Facelet with viewparam.xhtml?id=12 it will display the 12 onscreen. If you then input something valid, e.g. aaaaa, the id will disappear from the URL, but keeps being displayed on screen (owning to the stateful nature of ui components).

However... as OP mentioned, as soon as any validator error occurs (e.g. entering a), the id will be permanently lost. Entering valid input afterwards will not bring it back. It almost seems like a bug, but I tried both Mojarra 2.1 and Myfaces 2.1 and both have the same behavior.

Update:

After some inspection, the problem seems to be in this method of `UIViewParameter' (Mojarra):

public void encodeAll(FacesContext context) throws IOException {
    if (context == null) {
        throw new NullPointerException();
    }

    // if there is a value expression, update view parameter w/ latest value after render
    // QUESTION is it okay that a null string value may be suppressing the view parameter value?
    // ANSWER: I'm not sure.
    setSubmittedValue(getStringValue(context));
}

And then more specifically this method:

public String getStringValue(FacesContext context) {
    String result = null;
    if (hasValueExpression()) {
        result = getStringValueFromModel(context);
    } else {
        result = (null != rawValue) ? rawValue : (String) getValue();
    }
    return result;
}

Because hasValueExpression() is true, it will try to get the value from the model (the backing bean). But since this bean was request scoped it will not have any value for this request, since validation has just failed and thus no value has ever been set. In effect, the stateful value of UIViewParameter is overwritten by whatever the backing bean returns as a default (typically null, but it depends on your bean of course).

One workaround is to make your bean @ViewScoped, which is often a better scope anyway (I assume you use the parameter to get a user from a Service, and it's perhaps unnecessary to do that over and over again at every postback).

Another alternative is to create your own version of UIViewParameter that doesn't try to get the value from the model if validation has failed (as basically all other UIInput components do).

这篇关于验证错误后jsf viewparam丢失的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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