【问题标题】:JSF files inside WEB-INF directory, how do I access them?WEB-INF 目录中的 JSF 文件,我如何访问它们?
【发布时间】:2023-12-09 16:27:01
【问题描述】:

我想将我的 JSF 2.0 xhtml 文件放在 WEB-INF\jsf 下。那我该如何访问它们?我知道 WEB-INF 内部的任何东西都不会暴露在外部,所以我需要一个控制器来将我重定向到相应的 JSP,对吧? (这也是模型 2 模式 iirc)。

我可以通过 web.xml/faces-config.xml 中的参数来实现吗?我认为 FacesServlet 是我的 webapp 的控制器,所以它应该服务于这个目的?

还有一个关于理解 Model 2 模式的问题。是否每个操作都必须首先进入一个 servlet,然后由该 servlet 处理下一个可能的步骤?因此,在这种模式中禁止使用简单的<a href="anotherPage.html" />,因为它不会进入控制 servlet?

【问题讨论】:

    标签: java model-view-controller jsf-2 web-inf


    【解决方案1】:

    我想将我的 JSF 2.0 xhtml 文件放在 WEB-INF\jsf 下。那我该如何访问它们呢?

    你不能。无法直接访问/WEB-INF 文件夹中的文件。

    有两种方法可以解决 JSF 源文件可公开访问的问题。

    1. FacesServlet 映射到*.xhtml 而不是*.jsf

    2. 或者,通过web.xml 中的<security-constraint> 限制对*.xhtml 的直接访问。

      <security-constraint>
          <display-name>Restrict direct access to XHTML files</display-name>
          <web-resource-collection>
              <web-resource-name>XHTML files</web-resource-name>
              <url-pattern>*.xhtml</url-pattern>
          </web-resource-collection>
          <auth-constraint />
      </security-constraint> 
      

    另见:


    关于理解 Model 2 模式的另一个问题。是否每个操作都必须首先进入一个 servlet,然后由该 servlet 处理下一个可能的步骤?

    FacesServlet 已经这样做了。是控制器。使用 JSF,您已经得到了一个简单的 javabean 作为模型和 JSP/Facelets 文件作为视图。 FacesServlet 作为控制器,已经从您手中承担了请求参数收集、验证、转换、模型更新和导航的所有令人讨厌的工作。

    另见:


    所以在这种模式中禁止使用简单的&lt;a href="anotherPage.html" /&gt;,因为它不会进入控制 servlet?

    不,完全没问题。控制器将在需要时启动。如果资源不需要控制器(即静态资源),那么您也不需要让它通过某个控制器。


    以后,请在不同的 Stack Overflow 问题中提出多个问题。

    【讨论】:

    • 感谢您的回复。我已经只映射了 *.xhtml 文件(选项 1)。我已经读过,在某些框架(Struts、Spring Webflow、..)中,将 jsp 文件(=动态内容)放在 WEB-INF 下是可能的/首选方式,控制器在那里访问它们并将它们显示给用户。我只是想问一下 JSF 是否也可以这样做。但是如果我没看错的话,没有人可以通过浏览器查看 xhtml 文件的源代码,因为它们总是通过控制器呈现,对吧?
    【解决方案2】:

    要访问WEB-INF/jsf 文件夹中的xhtml 页面,您可以执行以下操作:

    1. xhtml pages 文件夹从webapp root 移动到WEB-INF
    2. 在项目中引入“Dispatcher View”模式
    3. 根据应用程序的页面将“Front Controller”servlet 映射到url
    4. Faces Servlet映射到“.xhtml
    5. 在“Dispatcher”内部将请求转发到来自“WEB-INF/jsf/&lt;name&gt;.xhtml”的页面
    6. 覆盖 jsf ViewHandler getActionUrl 以从生成的 action urlform, link, button)中排除“WEB-INF

    例如,xhtml 页面位于 webapp 根文件夹“jsf”中。页面之间的所有url 类似于jsf/&lt;pageName&gt;.xhtml。所以我们下一步:

    1. &lt;webapp root&gt;/jsf移动到&lt;webapp root&gt;/WEB-INF/jsf

    2. 创建 FrontController servlet:

    ``

    public class FrontController extends HttpServlet {
    
            @Override
            protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                process(req, resp);
            }
    
            @Override
            protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
                process(req, resp);
            }    
    
            private void process(HttpServletRequest request, HttpServletResponse response) {
                 Dispatcher dispatcher = Dispatcher.getInstance();
                 dispatcher.dispatch(request, response);
            }
    }
    
    1. 基于页面将 web.xml 中的 Front Controller servlet 映射到 url
    <servlet>
        <servlet-name>Front Controller</servlet-name>
        <servlet-class>controllers.FrontController</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>Front Controller</servlet-name>
        <url-pattern>/jsf/*</url-pattern>
    </servlet-mapping>
    
    1. web.xml中的Faces Servlet映射到.xhtml
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
    
    1. 创建Dispatcher,将request 转发到更正xhtml 页面:

    ``

    public class Dispatcher {
    
        public void dispatch(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String pageBase = "/WEB-INF/jsf/";
            String pagePath = null;
            String errorPage = "/WEB-INF/jsf/error.xthml";
    
            //here could be complicated logic to analyze if the page should be visible for security reasons, authorisation etc, business logic            
           //requested page could be taken from parsing requested URI
            //pageName = findPageNameFromURI(request.getRequestURI());
    
            pagePath = pageBase + pageName;
    
            //if page should not be visible
            pagePath = errorPage;            
    
            //forward to page inside WEB-INF/jsf
            request.getServletContext().getRequestDispatcher(pagePath).
                                       forward(request, response);        
        }   
    
    }
    

    因此,如果页面的 url 是 /myapp/jsf/home.xhtml,那么 Dispatcher 会将其转发到 myapp/WEB-INF/jsf/home.xhtml。 Faces Servlet 将处理“.xhtml”请求。但是,如果在页面上使用jsf 组件,如h:form, h:link, h:button 等生成actionurl,那么url 将真正包括“/WEB-INF”。所以要排除它,我们需要下一步。

    1. jsf 生成的url 中排除“/WEB-INF”(用于jsf 表单、链接、按钮)。 为此:

      6.1 创建jsf ViewHandler 的子类并覆盖getActionUrl

    ``

    public class HiddenPageViewHandler extends ViewHandlerWrapper {
    
        private static final String WEB_INF = "/WEB-INF";
    
        private ViewHandler parent;
    
        public HiddenPageViewHandler(ViewHandler parent) {
            this.parent = parent;
        }
    
        @Override
        public String getActionURL(FacesContext context, String viewId) {
            String actionUrl = super.getActionURL(context, viewId);
    
            if (actionUrl != null && actionUrl.contains(WEB_INF)) {
                actionUrl = actionUrl.replace(WEB_INF, "");
            }        
    
            return actionUrl;
        }
    
        @Override
        public ViewHandler getWrapped() {
            return parent;
        }
    
    }
    

    6.2 配置jsf 使用指定的ViewHandler。在faces-config.xml 中添加下一个:

       <application>
        ...
            <view-handler>
                controllers.HiddenPageViewHandler
            </view-handler>
       </application>
    

    【讨论】:

    • 不好的建议。这引入了一个安全漏洞:将所有 WEB-INF 内容直接公开。
    • 为了简单起见,调度程序只是在请求的 url 之前添加 WEB-INF。当然,调度员可能会根据需要完成复杂的工作。如何实现它只是一个想法。
    • 编辑了 Dispatcher 示例,使其仅从 WEB-INF/jsf 转发到页面,并增加了页面查找逻辑,包括安全检查或转发到错误页面
    • 在使用非 XHTML 映射的情况下隐藏公共页面的 JSF 源代码文件的具体问题仍然是一个相当笨拙的解决方案。当前的最佳答案显示了如何以更简单的方式解决它。
    • 当然,以上答案提供了更简单的方法。但我的解决方案只是展示使用 JSF 的 Dispatcher View 的好处。它的一个好处是可以从 WEB-INF 访问 jsf 文件。 Dispatcher View 模式也允许对 JSF 页面使用逻辑映射策略。在 Dispatcher 中,可以分析像 /myapp/jsf/home 这样的 url 并将其转发到 /WEB-INF/jsf/home.xhtml(对于 Faces Servlet)。在 ViewHandler 中,可以从 jsf 生成的 url 中删除 .xhtml 扩展名: if (actionUrl != null && actionUrl.contains(".xhtml")) { actionUrl = actionUrl.replace(".xhtml", ""); }
    最近更新 更多