Java f:viewParam,当调用第二个ajax请求时,带有转换器和ViewScoped异常

Java f:viewParam,当调用第二个ajax请求时,带有转换器和ViewScoped异常,java,validation,jsf,viewparams,omnifaces,Java,Validation,Jsf,Viewparams,Omnifaces,我有一个f:viewParam,我尝试绑定验证并将userId转换为Player,得到了一个意外的结果 <f:metadata> <f:viewParam name="userId" value="#{myBean.selectedPlayer}" converter="pConverter" converterMessage="Bad Request. Unknown User" required="true" requiredMe

我有一个
f:viewParam
,我尝试绑定验证并将
userId
转换为
Player
,得到了一个意外的结果

<f:metadata>
    <f:viewParam name="userId" value="#{myBean.selectedPlayer}" converter="pConverter"
         converterMessage="Bad Request. Unknown User" required="true"
         requiredMessage="Bad Request. Please use a link from within the system" />

</f:metadata>
<h:body>
    <p:messages id="msgs"/>        
    <h:form>
        <ul>
            <li><a href="index2.xhtml?userId=1">Harry</a></li>
            <li><a href="index2.xhtml?userId=2">Tom</a></li>
            <li><a href="index2.xhtml?userId=3">Peter</a></li>
        </ul>            
    </h:form>
    <h:form>
        <h:panelGrid columns="2" rendered="#{not empty myBean.selectedPlayer}">

            <h:outputText value="Id: #{myBean.selectedPlayer.id}"/>

            <h:outputText value="Name: #{myBean.selectedPlayer.name}"/>

        </h:panelGrid>
    </h:form>
    <h:form id="testForm">
        <h:inputText value="#{myBean.text}"/>
        <p:commandButton value="Switch" update=":msgs testForm"/>
        <h:outputText value="#{myBean.text}" rendered="#{not empty myBean.text}"/>
    </h:form>    
</h:body>

我的转换器看起来像这样

@FacesConverter(value="pConverter")
public class PConverter implements Converter {
private static final List<Player> playerList;
static{
    playerList = new ArrayList<Player>();        
    playerList.add(new Player(1, "Harry"));
    playerList.add(new Player(2, "Tom"));
    playerList.add(new Player(3, "Peter"));
}

@Override
public Object getAsObject(FacesContext fc, UIComponent uic, String value) {
    if(value == null || !value.matches("\\d+")){
        return null;
    }
    long id = Long.parseLong(value);
    for(Player p : playerList){
        if(p.getId() == id){
            return p;
        }
    }
    throw new ConverterException(new FacesMessage("Unknown userId: " + value));

}

@Override
public String getAsString(FacesContext fc, UIComponent uic, Object value) {
    if(!(value instanceof Player) || value == null){
        return null;
    }
    return String.valueOf(((Player)value).getId());
}
}
@facescoverter(value=“pConverter”)
公共类PConverter实现转换器{
私有静态最终列表playerList;
静止的{
playerList=newarraylist();
玩家列表。添加(新玩家(1,“哈利”);
玩家列表。添加(新玩家(2,“汤姆”);
玩家列表。添加(新玩家(3,“彼得”);
}
@凌驾
公共对象getAsObject(FacesContext fc、uic组件uic、字符串值){
if(value==null | |!value.matches(\\d+)){
返回null;
}
long id=long.parseLong(值);
对于(玩家p:玩家列表){
if(p.getId()==id){
返回p;
}
}
抛出新的ConverterException(新的FacesMessage(“未知用户ID:+值”);
}
@凌驾
公共字符串getAsString(FacesContext fc、uic组件uic、对象值){
如果(!(玩家的值实例)| |值==null){
返回null;
}
返回字符串.valueOf(((Player)value.getId());
}
}
当我点击三个链接(哈里、汤姆、彼得)时,转换器工作得很好。它转换id并将
播放器
绑定回我的托管bean。然后,我在文本框中键入内容,然后单击“切换”,当它第一次正常工作时,我键入的内容将显示在按钮旁边,但随后我更改了我键入的内容,再次单击“切换”,然后出现错误消息“请求错误”。请使用系统中的链接,这是
f:viewParam
所需的
的错误消息。如果我取出f:viewParam,那么一切都正常。令人惊讶的是,如果我从f:viewParam切换到o:viewParam(OmniFaces),那么它工作得很好

这是因为
在每个HTTP请求上运行,也在回发上运行。它适用于普通GET链接,因为您在链接中传递的正是该参数。对于POST表单,它会失败,因为您没有在按钮中传递该参数。因此,它在请求参数映射中变为
null
,并且
required
验证器启动,因此出现了此验证错误

为了让POST表单上的
用户满意,您基本上需要通过命令按钮/链接中的
保留初始请求参数


OmniFaces旨在与视图范围的bean结合使用,在
isRequired()
getter()中有一个额外的检查:


因此,这会在每次回发时跳过
required
验证器(此外,由于其无状态性质,它还会在每次回发时跳过设置模型值)。这就是为什么您没有看到验证错误,并且您仍然拥有正确的模型值(不会在每次回发时重置)。

非常感谢。Omnifaces真是太棒了,巴卢斯。我有一个关于
o:viewParam
的问题,在你的博客中,你说omnifaces将避免每次ajax请求调用时调用
Converter
,但是,它似乎总是进入方法
getAsString()
。所以我想知道这种行为是否正确。我知道设置模型值的是
getAsObject
,因此不调用
getAsObject
是一个很大的改进。感谢大家。当对象需要转换为字符串(以生成的HTML形式呈现)时,将调用
getAsString()
。但这应该是一个相当便宜的工作,因为它应该已经是它的一个属性的字符串值了。此时,对象已经在您的手中,因此不需要昂贵的数据库工作。
@Override
public boolean isRequired() {
    // The request parameter get lost on postbacks, however it's already present in the view scoped bean.
    // So we can safely skip the required validation on postbacks.
    return !FacesContext.getCurrentInstance().isPostback() && super.isRequired();
}