12

我有几个页面需要 userId 才能工作,因此下面的代码:

用户页面.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>

用户页面控制器.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中的view参数就消失了。由于其视图范围的性质,该 bean 仍然有效,但它破坏了我未来导航的尝试。当我四处搜索时,我得到的印象是视图参数应该在帖子之后保留,因此读取 userpage.jsf?userId=123,但事实并非如此。真正的预期行为是什么?

与此相关,我尝试在导航到要保留 userId 的另一个页面时实现视图参数的自动添加。它似乎对其他人有用,但对我来说, ViewRoot 中的 userId 始终为空。下面的代码用于检索视图参数(我知道我可以使用 bean 中临时存储的 userId 进行导航,但这个解决方案会更漂亮):

    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 在发布时使用正确的值调用。

我是否完全误解了视图参数的工作原理,或者这里是否存在某种错误?我认为我的用例应该非常普遍,并且在框架中有基本的支持。

4

2 回答 2

25

当我四处搜索时,我得到的印象是视图参数应该在帖子之后保留,因此读取 userpage.jsf?userId=123,但事实并非如此。真正的预期行为是什么?

这种行为是正确的。<h:form>生成一个<form>带有操作 URL 的 HTML 元素,没有任何视图参数。POST 请求只是提交到该 URL。如果你打算将视图参数保留在 URL 中,那么基本上有 3 种方式:

  1. 引入一些 ajax 魔法。

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

    这样,最初请求的页面以及浏览器地址栏中的请求 URL 始终保持不变。

  2. 如果适用(例如页面到页面导航),请将其设为 GET 请求并使用includeViewParams=true. 您可以为此使用<h:link>和:<h:button>

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

    但是,这在早于 2.1.6 的 Mojarra 版本中存在 EL 安全漏洞。确保您使用的是 Mojarra 2.1.6 或更高版本。另见问题 2247

  3. 控制<h:form>自己的动作 URL 的生成。提供一个自定义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();
    // ...
}
于 2012-04-27T15:50:09.807 回答
0

我是否正确,要使解决方案 1<f:ajax execute="@form" render="@form" />起作用,还必须包括类似的内容

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

里面<h:commandButton>

就像在https://stackoverflow.com/a/14026995/811046中描述的那样

于 2016-11-11T13:59:37.737 回答