Выполнение перенаправления, когда преобразование / проверка, связанная с параметрами запроса, не удалась
Ниже приведен простой случай использования <f:viewAction>
,
<f:metadata>
<f:viewParam name="id" value="#{testManagedBean.id}" maxlength="20"/>
<f:viewAction action="#{testManagedBean.viewAction}"/>
</f:metadata>
Вовлеченный боб
@ManagedBean
@ViewScoped
public final class TestManagedBean implements Serializable {
private static final long serialVersionUID = 1L;
private Long id; //Getter and setter.
public void viewAction() {
System.out.println("viewAction() called : " + id);
}
}
Параметр id
передается через URL. Существует ошибка преобразования, когда нечисловое значение, такое как xxx
передается через рассматриваемый URL и viewAction()
метод, связанный со слушателем <f:viewAction>
не вызывается.
Значение id
является null
в этом случае. Я хотел бы перенаправить на другую страницу, когда id
не конвертируется в нужный тип цели (как в этом случае) или id
не проверяется в соответствии с указанными критериями проверки, чтобы избежать потенциальных исключений, которые могут быть LazyDataModel#load()
метод PrimeFaces или где-то еще в связанном управляемом бине, когда осуществляется попытка доступа к этим параметрам в соответствующем управляемом бине. Для того чтобы это было так, viewAction()
метод должен быть вызван.
Как поступить с этим? Должен ли я использовать
<f:event type="preRenderView">
в сочетании с <f:viewAction>
?
2 ответа
Это заданное поведение. когда PROCESS_VALIDATIONS
фаза заканчивается неудачей валидации, оба UPDATE_MODEL_VALUES
а также INVOKE_APPLICATION
фазы пропущены. Точно так же, как в "обычных" формах с <h:form>
, Думать о <f:viewParam>
как <h:inputText>
и <f:viewAction>
как <h:commandButton>
и это станет более понятным.
Для вашего конкретного требования, выполняя перенаправление, если преобразование / проверка завершилась неудачно, существует как минимум 3 решения:
Как вы узнали, добавьте
<f:event listener>
, Я бы предпочел зацепитьpostValidate
событие вместо этого для лучшей самодокументируемости.<f:metadata> <f:viewParam name="id" value="#{bean.id}" maxlength="20" /> <f:event type="postValidate" listener="#{bean.redirectIfNecessary}" /> <f:viewAction action="#{bean.viewAction}" /> </f:metadata>
public void redirectIfNecessary() throws IOException { FacesContext context = FacesContext.getCurrentInstance(); if (!context.isPostback() && context.isValidationFailed()) { context.getExternalContext().redirect("some.xhtml"); } }
Проверка на
FacesContext#isPostback()
предотвращает перенаправление, выполняемое при сбоях проверки "обычных" форм в том же представлении (если оно есть).Расширить встроенный
LongConverter
посредством чего вы выполняете перенаправление вgetAsObject()
(валидатор не подходит как конвертер по умолчанию дляLong
уже потерпит неудачу на нечисловых входах в случае сбоя конвертера, валидаторы никогда не запускаются). Это, однако, плохой дизайн (жесткая связь).<f:metadata> <f:viewParam name="id" value="#{bean.id}" converter="idConverter" /> <f:viewAction action="#{bean.viewAction}" /> </f:metadata>
@FacesConverter("idConverter") public class IdConverter extends LongConverter { @Override public Object getAsObject(FacesContext context, UIComponent component, String value) { if (value == null || !value.matches("[0-9]{1,20}")) { try { context.getExternalContext().redirect("some.xhtml"); return null; } catch (IOException e) { throw new FacesException(e); } } else { return super.getAsObject(context, component, value); } } }
Вы можете при необходимости поиграть с
<f:attribute>
внутри<f:viewParam>
"передать" параметры преобразователю.<f:viewParam name="id" value="#{bean.id}" converter="idConverter"> <f:attribute name="redirect" value="some.xhtml" /> </f:viewParam>
String redirect = (String) component.getAttributes().get("redirect"); context.getExternalContext().redirect(redirect);
Создайте пользовательский тэгхендлер, который в основном делает то же
<f:event listener>
но без необходимости дополнительного метода поддержки.<html ... xmlns:my="http://example.com/ui"> <f:metadata> <f:viewParam name="id" value="#{bean.id}" maxlength="20" /> <my:viewParamValidationFailed redirect="some.xhtml" /> <f:viewAction action="#{bean.viewAction}" /> </f:metadata>
com.example.taghandler.ViewParamValidationFailed
public class ViewParamValidationFailed extends TagHandler implements ComponentSystemEventListener { private String redirect; public ViewParamValidationFailed(TagConfig config) { super(config); redirect = getRequiredAttribute("redirect").getValue(); } @Override public void apply(FaceletContext context, UIComponent parent) throws IOException { if (parent instanceof UIViewRoot && !context.getFacesContext().isPostback()) { ((UIViewRoot) parent).subscribeToEvent(PostValidateEvent.class, this); } } @Override public void processEvent(ComponentSystemEvent event) throws AbortProcessingException { FacesContext context = FacesContext.getCurrentInstance(); if (context.isValidationFailed()) { try { context.getExternalContext().redirect(redirect); } catch (IOException e) { throw new AbortProcessingException(e); } } } }
/WEB-INF/my.taglib.xml
<?xml version="1.0" encoding="UTF-8"?> <facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd" version="2.0" > <namespace>http://example.com/ui</namespace> <tag> <tag-name>viewParamValidationFailed</tag-name> <handler-class>com.example.taghandler.ViewParamValidationFailed</handler-class> </tag> </facelet-taglib>
/WEB-INF/web.xml
<context-param> <param-name>javax.faces.FACELETS_LIBRARIES</param-name> <param-value>/WEB-INF/my.taglib.xml</param-value> </context-param>
Правда, это немного кода, но в итоге получается чистый и многократно используемый
<my:viewParamValidationFailed>
тег и на самом деле хорошо подходит для новой функции OmniFaces.
Почему бы просто не проверить id
сам?
@ManagedBean
@ViewScoped
public final class TestManagedBean implements Serializable
{
private String id; //Getter and setter.
private Long validId; //Getter and setter.
public void viewAction() {
try {
validId = Long.parseLong(id);
} catch (NumberFormatException ex) {
FacesContext facesContext = FacesContext.getCurrentInstance();
String outcome = "redirect.xhtml";
facesContext.getApplication().getNavigationHandler().handleNavigation(facesContext, null, outcome);
}
}
}