Подтвердить что ты не робот

Обработка параметров просмотра в JSF после публикации

У меня есть несколько страниц, которым нужно работать с userId, таким образом, следующий код:

userpage.xhtml

<!-- xmlns etc. omitted -->
<html>
<f:metadata>
    <f:viewParam name="userId" value="#{userPageController.userId}"/>
</f:metadata>
<f:view contentType="text/html">
<h:head>
</h:head>
<h:body>
    <h:form>
        <h:commandButton action="#{userPageController.doAction}" value="post"/>
    </h:form>
</h:body>
</f:view>

userPageController.java

@Named
@ViewScoped
public class userPageControllerimplements Serializable {
    private static final long serialVersionUID = 1L;

    @Inject protected SessionController sessionController;
    @Inject private SecurityContext securityContext;
    @Inject protected UserDAO userDAO;

    protected User user;
    protected Long userId;

    public UserPage() {
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        if(!FacesContext.getCurrentInstance().isPostback()){
            User u = userDAO.find(userId);
            this.userId = userId;
            this.user = u;
        }
    }

    public void doAction(){

    }

}

Однако, после вызова doAction, параметр вида в URL-адресе исчезает. bean по-прежнему работает из-за его видимой природы, но он разрушает мои попытки будущей навигации. Когда я ищу вокруг, у меня создается впечатление, что параметр просмотра должен оставаться после сообщения, таким образом, считывая userpage.jsf? UserId = 123, но это не так. Каково действительно предполагаемое поведение?

В связи с этим я попытался реализовать автоматическое добавление параметров просмотра при переходе на другую страницу, где я хочу сохранить userId. Кажется, что это работает для других, но для меня userId в ViewRoot всегда имеет значение null. Код ниже используется для извлечения viewparameter (я знаю, что я мог бы использовать временно сохраненный userId в bean для навигации, но это решение было бы намного более привлекательным):

    String name = "userId";
    FacesContext ctx = FacesContext.getCurrentInstance();
    ViewDeclarationLanguage vdl = ctx.getApplication().getViewHandler().getViewDeclarationLanguage(ctx, viewId);
    ViewMetadata viewMetadata = vdl.getViewMetadata(ctx, viewId);
    UIViewRoot viewRoot = viewMetadata.createMetadataView(ctx);
    UIComponent metadataFacet = viewRoot.getFacet(UIViewRoot.METADATA_FACET_NAME);

    // Looking for a view parameter with the specified name
    UIViewParameter viewParam = null;
    for (UIComponent child : metadataFacet.getChildren()) {
        if (child instanceof UIViewParameter) {
            UIViewParameter tempViewParam = (UIViewParameter) child;
            if (name.equals(tempViewParam.getName())) {
                viewParam = tempViewParam;
                break;
            }
        }
    }

    if (viewParam == null) {
        throw new FacesException("Unknown parameter: '" + name + "' for view: " + viewId);
    }

    // Getting the value
    String value = viewParam.getStringValue(ctx);  // This seems to ALWAYS be null.

Последняя мысль состоит в том, что методы setter все еще работают, setUserId вызывается с правильным значением в сообщении.

Неужели я полностью не понял, как работают параметры представления, или есть какая-то ошибка здесь? Я думаю, что мой прецедент должен быть чрезвычайно распространенным и иметь базовую поддержку в рамках.

4b9b3361

Ответ 1

Когда я ищу вокруг, у меня создается впечатление, что параметр просмотра должен оставаться после сообщения, таким образом, считывая userpage.jsf? userId = 123, но это не так. Каково действительно предполагаемое поведение?

Это правильно. <h:form> генерирует элемент HTML <form> с URL-адресом действия без любых параметров представления. Запрос POST просто отправляет именно этот URL. Если вы намерены сохранить параметры представления в URL-адресе, то есть в основном 3 способа:

  • Принесите некоторую магию ajax.

    <h:commandButton action="#{userPageController.doAction}" value="post">
        <f:ajax execute="@form" render="@form" />
    </h:commandButton>
    

    Таким образом, первоначально запрошенная страница и, следовательно, URL-адрес запроса в адресной строке браузера остаются неизменными все время.

  • Если это применимо (например, для навигации по страницам), сделайте запрос GET и используйте includeViewParams=true. Вы можете использовать <h:link> и <h:button> для этого:

    <h:button outcome="nextview?includeViewParams=true" value="post" />
    

    Однако у этого есть эксплойт безопасности EL в версиях Mojarra старше 2.1.6. Убедитесь, что вы используете Mojarra 2.1.6 или новее. См. Также проблема 2247.

  • Управляйте созданием URL-адреса действия <h:form> самостоятельно. Предоставьте пользовательский ViewHandler (просто растяните ViewHandlerWrapper), в котором вы выполняете работу в getActionURL().

    public String getActionURL(FacesContext context, String viewId) {
        String originalActionURL = super.getActionURL(context, viewId);
        String newActionURL = includeViewParamsIfNecessary(context, originalActionURL);
        return newActionURL;
    }
    

    Чтобы запустить его, зарегистрируйте его в faces-config.xml следующим образом:

    <application>
        <view-handler>com.example.YourCustomViewHandler</view-handler>
    </application>
    

    Это также то, что OmniFaces <o:form> делает. Он поддерживает дополнительный атрибут includeViewParams, который включает все параметры представления в URL-адрес формы:

    <o:form includeViewParams="true">
    

Обновление: получение параметров представления текущего представления программно (что в основном является вашим вторым вопросом) должно выполняться следующим образом:

Collection<UIViewParameter> viewParams = ViewMetadata.getViewParameters(FacesContext.getCurrentInstance().getViewRoot());

for (UIViewParameter viewParam : viewParams) {
    String name = viewParam.getName();
    Object value = viewParam.getValue();
    // ...
}

Ответ 2

Правильно ли, что для решения 1 <f:ajax execute="@form" render="@form" /> для работы нужно было бы включить что-то вроде

<f:param name="userId" value="#{userPageController.userId}"/>

внутри <h:commandButton>?

Как описано в fooobar.com/info/214996/...