includeViewParams=true преобразует пустое значение модели в пустую строку в строке запроса
Учитывая <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"/>
Соответствующий управляемый компонент:
@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";
}
}
Параметр как указано <f:viewParam>
не является обязательным. Например, доступ к странице осуществляется с помощью URL-адреса следующим образом.
https://localhost:8181/Project-war/private_resources/Test.jsf
поскольку id
является необязательным параметром, к URL-адресу прикрепляется пустой параметр (при изменении языка с <p:selectOneMenu>
), если он не поставляется следующим образом.
https://localhost:8181/Project-war/private_resources/Test.jsf?id=
Этого не должно быть. Пустой параметр не должен добавляться, если он не указан, а URL должен выглядеть как первый.
Есть ли способ предотвратить добавление пустого параметра к URL, если он не был передан?
Это связано только с конвертером, как указано в <f:viewParam>
- javax.faces.Long
,
Если этот преобразователь удаляется, параметры не добавляются к URL-адресу, если параметры не указаны.
Хотя указывать преобразователь, как показано здесь, совершенно не нужно, у меня есть преобразователи, как показано ниже для преобразования id
хотя URL-адрес передан в качестве параметра строки запроса в объект 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() : "";
}
}
Этот конвертер теперь должен быть указан явно с <f:viewParam>
следующее.
<f:viewParam name="id"
value="#{testManagedBean.id}"
converter="#{zoneConverter}"
rendered="#{not empty param.id}"/>
И связанный управляемый компонент должен быть изменен следующим образом.
@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";
}
}
1 ответ
Это, вероятно, упущение в реализации Мохарры по умолчанию UIViewParameter#getStringValueFromModel()
чей источник для справки скопирован ниже:
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 }
Этот метод вызывается для каждого UIViewParameter
(компонент пользовательского интерфейса позади <f:viewParam>
) во время построения строки запроса для includeViewParams=true
, Мы видим в источнике, что он вызывает конвертер независимо от того, currentValue
является null
или нет. Другими словами, даже если значение модели null
, он по-прежнему вызывает конвертер с ним.
В соответствии с Javadoc Converter#getAsString()
преобразователи по спецификации должны возвращать строку нулевой длины, если значение равно null
:
getAsString
...
Возвращает: Строка нулевой длины, если значение
null
в противном случае результат преобразования
Таким образом, конвертеры фактически никогда не возвращаются null
на getAsString()
, Затем они возвращают пустую строку. В случае просмотра параметров в строке запроса это крайне нежелательно. Разница между значением пустой строки и полным отсутствием в строке запроса действительно значительна.
Я сообщил об этом парням из Моджарры как вопрос 3288. Затем они должны решить эту проблему следующим образом:
391 Object currentValue = ve.getValue(context.getELContext());
392
393 if (currentValue == null) {
394 return null;
395 }
В то же время, я совершил решение для OmniFaces. <o:viewParam>
был расширен с этим исправлением. Это доступно согласно сегодняшнему снимку 1.8.
<f:metadata>
<o:viewParam name="id" value="#{testManagedBean.id}" converter="javax.faces.Long"/>
</f:metadata>
Обновление: они решили не исправить это. В любом случае, есть OmniFaces.