JSF без @ViewScoped - PullRequest
       137

JSF без @ViewScoped

5 голосов
/ 05 мая 2020

Я использую JSF в течение многих лет, и в следующем проекте мы стремимся сделать веб-уровень как можно более безгражданским. Одна из возможностей, которую я изучаю, - это удаление @ViewScoped beans в пользу @RequestScoped (плюс один или два @SessionScoped beans, если требуется). Это создает проблемы для сложных страниц с AJAX, таблицами данных и условным рендерингом. Мой вопрос: насколько хорошо JSF (и PrimeFaces) работает с веб-компонентами без сохранения состояния? Должен ли я продолжить изучение этого вопроса, или это @ViewScope сейчас настолько фундаментально, что не стоит затраченных усилий?

Я понимаю, когда я пишу этот вопрос, что он может быть закрыт как «основанный главным образом на мнении» , однако я надеюсь, что это не так, меня интересуют конкретные c проблемы, которые были @ViewScope решены, и какие исторические c обходные пути мне пришлось бы повторно ввести, игнорируя @ViewScoped.

1 Ответ

4 голосов
/ 09 мая 2020

Насколько хорошо JSF (и PrimeFaces) работает с веб-компонентами без сохранения состояния?

Это технически возможно.

JSF использует состояние просмотра в первую очередь для отслеживания атрибутов «отключено», «только для чтения» и «обработано» компонентов UIInput и UICommand, а также «отправленное значение», «локальное значение» и «действительно?» состояния компонентов EditableValueHolder (реализованных, среди прочего, UIInput).

В случае атрибутов «отключено», «только для чтения» и «обработано», если они представляют собой EL выражение, то JSF перепроверит его во время обработки запроса на отправку формы. Ниже приведен базовый пример c:

<h:form>
    <h:commandButton value="toggle" action="#{bean.toggle}">
        <f:ajax render="panel" />
    </h:commandButton>
    <h:panelGroup id="panel">
        <h:commandButton value="submit" action="#{bean.submit}" rendered="#{bean.toggled}">
            <f:ajax />
        </h:commandButton>
    </h:panelGroup>
</h:form>
@Named
@ViewScoped
public class Bean implements Serializable {

    private static final long serialVersionUID = 1L;

    private boolean toggled;

    public void toggle() {
        this.toggled = !toggled;
    }

    public void submit() {
        System.out.println("Submitted");
    }

    public boolean isToggled() {
        return toggled;
    }
}

Сначала нажмите кнопку «Переключить», а затем кнопку «Отправить». В случае bean-компонента с областью просмотра он будет работать нормально. Если вы, однако, замените здесь @ViewScoped на @RequestScoped, тогда это не удастся, потому что toggled по умолчанию возвращается к false в тот момент, когда JSF необходимо декодировать кнопку «отправить» во время запроса обратной передачи, и поэтому его * Атрибут 1025 * будет оценивать false, и в конечном итоге JSF не будет помещать событие действия в очередь.

В таком случае вам нужно убедиться, что свойство предварительно инициализировано до ожидаемого значения во время (пост) создания bean-компонент запроса. Один из способов - использовать для этого скрытые поля ввода в обновленном ajax компоненте. Вот скорректированный пример:

<h:form>
    <h:commandButton value="toggle" action="#{bean.toggle}">
        <f:ajax render="panel" />
    </h:commandButton>
    <h:panelGroup id="panel">
        <input type="hidden" name="toggled" value="#{bean.toggled}" />
        <h:commandButton value="submit" action="#{bean.submit}" rendered="#{bean.toggled}">
            <f:ajax />
        </h:commandButton>
    </h:panelGroup>
</h:form>
@Named
@RequestScoped
public class Bean {

    @Inject @ManagedProperty("#{param.toggled}")
    private boolean toggled;

    public void toggle() {
        this.toggled = !toggled;
    }

    public void submit() {
        System.out.println("Submitted");
    }

    public boolean isToggled() {
        return toggled;
    }
}

ПРИМЕЧАНИЕ: <h:inputHidden>, к сожалению, не будет работать, поскольку он обновляет значение модели только после событие действия должно быть в очереди. Даже не с immediate="true" на нем. Это, кстати, подводит меня к идее нового <o:inputHidden> для OmniFaces.

С этими изменениями все будет работать нормально.

Однако , поскольку состояние, которое изначально было ограничено просмотром (свойство toggled), теперь стало параметром запроса, оно полностью открыто для мира и, следовательно, также может быть изменено хакерами. Хакеры, желающие активировать кнопку «Отправить», не нажимая сначала кнопку «Переключить», теперь могут просто вручную добавить параметр запроса toggled=true. Желательно ли это, зависит от бизнес-требований вашего приложения, но чаще всего это совершенно нежелательно.

Это то, от чего JSF пытается вас защитить, предлагая возможность поместить эти чувствительные свойства в @ViewScoped bean вместо этого.

Это оказывается проблематичным для сложных страниц с AJAX, таблицами данных и условным рендерингом

Верно, но все же технически невозможно. Вам нужно только вручную переносить разбитые на страницы, отсортированные и отфильтрованные состояния через вручную заполненные скрытые поля ввода, как показано выше. <p:dataTable> поддерживает привязку этих состояний к свойствам компонента. Например:

<p:dataTable ...
    first="#{bean.first}"
    sortField="#{bean.sortField}"
    sortOrder="#{bean.sortOrder}"
    filterBy="#{bean.filterBy}">
    ...
</p:dataTable> 

. Вы можете просто скопировать их в поля <input type="hidden">, как было показано ранее (которые, как вы убедитесь, охватываются <p:ajax update>!), И, наконец, получить их через @ManagedProperty и / или @PostConstruct.

В эффектах вы, таким образом, в основном заново изобретаете работу, которая в настоящее время уже выполняется с помощью javax.faces.ViewState скрытого поля ввода в сочетании с @ViewScoped beans. Так почему бы не использовать его прямо сейчас? :)

Если вас больше всего беспокоит использование памяти, вам необходимо тщательно спроектировать bean-компоненты таким образом, чтобы только состояние с ограниченным представлением сохранялось в bean-компоненте @ViewScoped и только состояние области запроса сохраняется в bean-компоненте @RequestScoped. Например, вполне нормально поместить модель данных в bean-компонент с областью действия запроса и состояние разбивки на страницы / сортировки / фильтрации в bean-компоненте с областью просмотра. Вы также можете рассмотреть OmniFaces @ViewScoped вместо этого, поскольку это сразу же уничтожает состояние представления и физический компонент при выгрузке страницы.

Тем не менее, имея в виду этот вопрос, я 've всего за несколько часов go проверил и улучшил библиотеку OptimusFaces , чтобы убедиться, что она также полностью поддерживает представления без сохранения состояния с <f:view transient="true">, а также новый интеграционный тест. Преимущество OptimusFaces, помимо прочего, в том, что вам больше не нужно вручную беспокоиться о переносе состояния разбивки на страницы / сортировки / фильтрации. OptimusFaces позаботится об этом за вас.

См. Также:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...