【问题标题】:includeViewParams=true converts null model value to empty string in query stringincludeViewParams=true 将空模型值转换为查询字符串中的空字符串
【发布时间】:2023-03-18 19:48:02
【问题描述】:

给定一个<p:selectOneMenu>,如下所示。

<f:metadata>
    <f:viewParam name="id" value="#{testManagedBean.id}" converter="javax.faces.Long"/>
</f:metadata>

<p:selectOneMenu value="#{localeBean.language}" onchange="changeLanguage();">
    <f:selectItem itemValue="en" itemLabel="English" />
    <f:selectItem itemValue="hi" itemLabel="Hindi" />
</p:selectOneMenu>

<p:remoteCommand action="#{testManagedBean.submitAction}"
                 name="changeLanguage"
                 process="@this"
                 update="@none"/>

对应的托管bean:

@ManagedBean
@RequestScoped
public final class TestManagedBean {

    private Long id; //Getter and setter.

    public TestManagedBean() {}

    public String submitAction() {
        return FacesContext.getCurrentInstance().getViewRoot().getViewId() + "?faces-redirect=true&includeViewParams=true";
    }
}

&lt;f:viewParam&gt; 表示的参数是可选的。例如,使用如下 URL 访问页面。

https://localhost:8181/Project-war/private_resources/Test.jsf

由于id是一个可选参数,一个空参数附加到URL(当从&lt;p:selectOneMenu&gt;更改语言时),以防它如下提供。

https://localhost:8181/Project-war/private_resources/Test.jsf?id=

这不应该发生。如果未提供空参数且 URL 应与第一个类似,则不应附加空参数。

有没有办法防止空参数在未传递的情况下附加到 URL 中?


这仅与&lt;f:viewParam&gt; - javax.faces.Long 指定的转换器相关联。

如果移除此转换器,则不会将参数附加到 URL,以防不提供任何参数。

虽然完全没有必要指定此处演示的转换器,但我有如下所示的转换器将通过 URL 作为查询字符串参数传递的 id 转换为 JPA 实体。

@ManagedBean
@RequestScoped
public final class ZoneConverter implements Converter {

    @EJB
    private final SharableBeanLocal sharableService = null;

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        try {
            long parsedValue = Long.parseLong(value);

            if (parsedValue <= 0) {
                throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Message Summary", "Message"));
            }

            ZoneTable entity = sharableService.findZoneById(parsedValue);
            if (entity == null) {
                throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_WARN, "Message Summary", "Message"));
            }

            return entity;
        } catch (NumberFormatException e) {
            throw new ConverterException(new FacesMessage(FacesMessage.SEVERITY_ERROR, "Message Summary", "Message"), e);
        }
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        return value instanceof ZoneTable ? ((ZoneTable) value).getZoneId().toString() : "";
    }
}

现在需要使用&lt;f:viewParam&gt; 明确指定此转换器,如下所示。

<f:viewParam name="id" 
             value="#{testManagedBean.id}"
             converter="#{zoneConverter}"
             rendered="#{not empty param.id}"/>

并且关联的托管bean需要进行如下更改。

@ManagedBean
@RequestScoped
public final class TestManagedBean {

    private ZoneTable id;  //Getter and setter.

    public TestManagedBean() {}

    public String submitAction() {
        return FacesContext.getCurrentInstance().getViewRoot().getViewId() + "?faces-redirect=true&includeViewParams=true";
    }
}

【问题讨论】:

  • 您是否尝试过将required="false" 用于您的&lt;f:viewParam&gt;
  • 尝试将required&lt;f:viewParam&gt; 设置为false,但这也没有什么不同。
  • 您在帖子中遇到的一些错误/不是最佳实践:1. 从 JSF 2 开始,*.xhtml 被用作映射模式,而不是 *.jsf。 X。 2. 你忘了告诉#{bean} 的实现。 3. 您为您的问题包装了很多不相关的代码:托管属性与您的案例无关,p:remoteCommand 也不相关。删除它们并最小化您的测试用例,直到您可以用最少的代码重现问题。如果您继续拥有它,请按原样发布。见SSCCEquestionformatting.PD:我对你的问题做了一个测试用例,没有问题。

标签: jsf jsf-2.2 viewparams


【解决方案1】:

这可能是UIViewParameter#getStringValueFromModel()Mojarra's default implementation 的疏忽,其来源供参考,复制粘贴如下:

384    public String getStringValueFromModel(FacesContext context)
385        throws ConverterException {
386        ValueExpression ve = getValueExpression("value");
387        if (ve == null) {
388            return null;
389        }
390
391        Object currentValue = ve.getValue(context.getELContext());
392
393        // If there is a converter attribute, use it to to ask application
394        // instance for a converter with this identifer.
395        Converter c = getConverter();
396
397        if (c == null) {
398            // if value is null and no converter attribute is specified, then
399            // return null (null has meaning for a view parameters; it means remove it).
400            if (currentValue == null) {
401                return null;
402            }
403            // Do not look for "by-type" converters for Strings
404            if (currentValue instanceof String) {
405                return (String) currentValue;
406            }
407
408            // if converter attribute set, try to acquire a converter
409            // using its class type.
410
411            Class converterType = currentValue.getClass();
412            c = context.getApplication().createConverter(converterType);
413
414            // if there is no default converter available for this identifier,
415            // assume the model type to be String.
416            if (c == null) {
417                return currentValue.toString();
418            }
419        }
420
421        return c.getAsString(context, this, currentValue);
422    }

在为includeViewParams=true 构建查询字符串期间,每个UIViewParameter&lt;f:viewParam&gt; 后面的 UI 组件)都会调用此方法。我们在源代码中看到,无论currentValue 是否为null,它都会调用转换器。也就是说,即使模型值为null,它仍然会用它调用转换器。

根据javadoc of Converter#getAsString(),如果值为null,则转换器按规范要求返回零长度字符串:

getAsString

...

返回:如果值为null,则返回零长度字符串,否则为转换结果

因此,转换器实际上应该永远不会在 getAsString() 上返回 null。然后他们返回一个空字符串。对于查询字符串中的视图参数,这是非常不可取的。空字符串值和查询字符串中完全不存在之间的区别非常显着。

我已经以issue 3288 向 Mojarra 家伙报告了它。然后他们应该按如下方式解决这个问题:

391        Object currentValue = ve.getValue(context.getELContext());
392
393        if (currentValue == null) {
394            return null;
395        }

与此同时,我有committed OmniFaces 的解决方案。 &lt;o:viewParam&gt; 已通过此修复扩展。可通过今天的1.8 snapshot 获取。

<f:metadata>
    <o:viewParam name="id" value="#{testManagedBean.id}" converter="javax.faces.Long"/>
</f:metadata>

更新:他们决定不修复它。无论如何,都有 OmniFaces。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2016-05-25
  • 2014-11-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-14
  • 1970-01-01
  • 2015-07-17
相关资源
最近更新 更多