如何拦截 JSF 视图解析以替换结果页面?

How to intercept JSF view resolving in order to replace the outcome page?

某个托管 bean 具有 returns "/private/myview.jsf" 的操作方法。但是,不更改此代码,我想执行一些检查并最终呈现视图 "/private/other/myview.jsf"

所以,总而言之,我想在某处翻译"/private/myview.jsf""/private/other/myview.jsf"方法return和实际视图渲染之前。

如何实现?


环境:

  1. 日蚀月神
  2. Java1.7
  3. JSF 2

现状

Web 应用程序完全可用,但开发以支持辅助功能。

下一版本的要求

成为一个足够优秀的应用程序,供使用屏幕的盲人使用 readers。

详情

经过一番研究,我们得出的结论是,我们现在必须拥有每个页面的无障碍版本。

我们会为每个页面设计这样的无障碍版本,问题是什么时候显示无障碍版本,什么时候显示无障碍版本。

我们决定当用户单击顶部的某个 link 时,应用程序将转为 可访问模式 (这将不是默认状态)页。在可访问模式下,仅呈现可访问版本的页面。

我们不想审查所有的应用程序,我们想要的是拦截 JSF 的某些阶段并替换应该呈现的结果。例如,考虑以下内容:

  1. 某个页面有一个 link 或另一个页面的按钮,比方说 "mysettings.xhtml";
  2. 在可访问模式下,我们想告诉 JSF 不要渲染 "mysettings.xhtml",而是应该渲染 "mysettings_ac.xhtml""accessible/mysettings.xhtml"
  3. 这两个页面将与完全相同的托管 bean 进行交互,并将提供完全相同的功能,但一个对那些可以看到的人来说很好,另一个将被设计为易于屏幕显示 reader用户。

提前致谢!

So, in summary, I want to translate "/private/myview.jsf" to "/private/other/myview.jsf" somewhere after the method return and before the actual view rendering.

如果您已经在使用 JSF 2.2,则可以根据某些条件提供自定义 ResourceHandeler wherein you return the desired view resource in createViewResource()

public class YourResourceHandler extends ResourceHandlerWrapper {

    private ResourceHandler wrapped;

    public YourResourceHandler(ResourceHandler wrapped) {
        this.wrapped = wrapped;
    }

    @Override
    public ViewResource createViewResource(FacesContext context, final String name) {
        if (someCondition) {
            name = name.replace("/private/", "/private/other/");
        }

        return super.createViewResource(context, name);
    }

    @Override
    public ResourceHandler getWrapped() {
        return wrapped;
    }

}

faces-config.xml中注册如下:

<application>
    <resource-handler>com.example.YourResourceHandler</resource-handler>
</application>

或者,如果您还没有使用 JSF 2.2,请改用 ResourceResolver

public class YourResourceResolver extends ResourceResolver {

    private ResourceResolver parent;

    public YourResourceResolver(ResourceResolver parent) {
        this.parent = parent;
    }

    @Override
    public URL resolveUrl(String path) {
        if (someCondition) {
            path = path.replace("/private/", "/private/other/");
        }

        return parent.resolveUrl(path);
    }

}

web.xml中注册如下:

<context-param>
    <param-name>javax.faces.FACELETS_RESOURCE_RESOLVER</param-name>
    <param-value>com.example.YourResourceResolver</param-value>
</context-param>

如我所见,可能有两种解决方案。

  1. 使用redirect()方法

    returnAction(){ ExternalContext context = FacesContext.getCurrentInstance().getExternalContext(); context.redirect("/private/other/myview.jsf"); //Relative path might vary based upon your business logic }

  2. 操作后有条件重定向return;我们可以在 faces-config.xml 中维护一个导航规则,如下所示:

    <navigation-rule>
        <navigation-case>
            <from-action>#{yourManagedBean.actionMethod}</from-action>
            <if>#{yourManagedBean.redirectAccessibleFlag}</if>   <!-- We maintain a boolean variable redirectAccessibleFlag in the managed Bean and set its value to true or false conditionally-->
            <to-view-id>/private/other/myview.jsf</to-view-id>
           <redirect/>
        </navigation-case>
    </navigation-rule>

在第二种选择中,您可以在 ApplicationScope 中放置托管 bean,以便跨多个 JSF 页面使用。

虽然这些解决方案来自于一般概念,但不一定属于"somewhere after the method return and before the actual view rendering.",但你可以检查它是否解决了目的。