【问题标题】:URL rewriting solution needed for JSFJSF 需要的 URL 重写解决方案
【发布时间】:2012-04-11 22:52:12
【问题描述】:

假设以下应用环境:

+-----------------+
| App server      |
+-----------------+
|                 |                                   +-------+
| ear1            |                                   |       |
|  +-web1 (/ctx1) +--<-- http://localhost/ctx1/xxx/ --+       +--<-- http://www.example.com/xxx/
|                 |                                   |       |
|                 |                                   | proxy |
| ear2            |                                   |       |
|  +-web2 (/ctx2) +--<-- http://localhost/ctx2/yyy/ --+       +--<-- http://abc.example.com/yyy/
|                 |                                   |       |
+-----------------+                                   +-------+

如您所见,代理(在我的例子中为nginx)将请求转发到单个应用程序服务器实例,该实例又具有多个具有不同上下文路径的 Web 模块。当然我不希望我的公共服务器暴露内部上下文根和代理它的工作是否很好,包装和解包http请求等。但仍然存在一个大问题:JSF生成的html代码(链接,css,js资源,表单actions) 包含上下文路径,在我的例子中是 /ctx1/ctx2。这就是我想要避免的。

除了使用越来越多不同的应用程序服务器实例(域)之外,我目前没有任何解决方案,导致我的硬件资源逐渐消失。据我了解,我需要使用一些包装器来扩展我的 JSF 应用程序,这些包装器可能在 faces-config.xml 中注册,这将删除生成的 html 中的上下文前缀。也欢迎任何其他解决方案。

请指点我正确的方向。

【问题讨论】:

    标签: java jsf web-applications jsf-2 prettyfaces


    【解决方案1】:

    我发布的解决方案可能对面临同样问题的其他人有所帮助。我需要做的就是实现我自己的javax.faces.application.ViewHandler 并在faces-config.xml 中注册:

    public class CustomViewHandler extends ViewHandlerWrapper {
      private ViewHandler wrappped;
    
      public CustomViewHandler(ViewHandler wrappped) {
        super();
        this.wrappped = wrappped;
      }
    
      @Override
      public ViewHandler getWrapped() {
        return wrappped;
      }
    
      @Override
      public String getActionURL(FacesContext context, String viewId) {
        String url =  super.getActionURL(context, viewId);
        return removeContextPath(context, url);
      }
    
      @Override
      public String getRedirectURL(FacesContext context, String viewId, Map<String, List<String>> parameters, boolean includeViewParams) {
        String url =  super.getRedirectURL(context, viewId, parameters, includeViewParams);
        return removeContextPath(context, url);
      }
    
      @Override
      public String getResourceURL(FacesContext context, String path) {
        String url = super.getResourceURL(context, path);
        return removeContextPath(context, url);
      }
    
      private String removeContextPath(FacesContext context, String url) {
        ServletContext servletContext = (ServletContext) context.getExternalContext().getContext();
        String contextPath = servletContext.getContextPath();
        if("".equals(contextPath)) return url; // root context path, nothing to remove
        return url.startsWith(contextPath) ? url.substring(contextPath.length()) : url;
      }
    }
    

    faces-config.xml:

    <faces-config xmlns="http://java.sun.com/xml/ns/javaee"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
                  version="2.0">
      <application>
        <view-handler>test.CustomViewHandler</view-handler>
      </application>
    </faces-config>
    

    【讨论】:

    • 注意:这也可以用Filter 和不到一半的代码来完成。
    • @BalusC,今天我正要实现你的过滤器解决方案,但被你的帖子被删除的事实吓坏了))
    • 我只是删除它,因为它毕竟不值得努力。
    • 使用预先构建的 URL 重写过滤器!有很多选择!
    • @BalusC,嗨,仅供参考:刚刚按照您的建议测试了Fliter,除了部分请求外一切正常,似乎 ajax 不尊重请求的上下文路径。
    【解决方案2】:

    您可以为此使用OCPsoft Rewrite URLRewriteFilter(目前不是 PrettyFaces,但您可以同时使用它们,直到它们在 PrettyFaces 4 发布后正式结合在一起 - Rewrite 是 PrettyFaces 4 的核心项目)

    使用单个配置规则执行此类操作应该相当简单。如果这条规则太严格或太笼统,你显然可以摆弄。

    .defineRule()
    .when(URL.matches("{prefix}" + context.getContextPath() + "{suffix}")
    .perform(Substitute.with("{prefix}{suffix}"))
    

    查看重写站点。这很容易设置。 http://ocpsoft.org/rewrite/

    【讨论】:

    • 嗨林肯!它也适用于我页面上的 css、js 和其他资源吗?
    • 只要这些链接是通过 JSF 呈现的,而不是在 HTML 中硬编码,那么绝对可以。否则,您可以设置其他规则来处理这种情况。
    【解决方案3】:

    我遇到了同样的问题并尝试了您的解决方案。虽然它或多或少地工作,但仍然存在一些小故障。老实说,这感觉更像是对抗症状而不是治愈疾病。

    这就是最终对我有用的方法:

    我没有通过路径将部署分开,而是将每个部署分配到自己的端口:

    foo.war <-- http://localhost:8080/ -- | Proxy | <-- http://www.foo.com -- | Client |
    bar.war <-- http://localhost:8181/ -- | Proxy | <-- http://www.bar.com -- | Client |
    

    这样,两个部署都可以使用 / 作为其上下文路径,因此无需编辑上下文路径。

    要实现这一点,您不必运行两个应用程序服务器。在我的例子中(Wildfly 10.0),在 wildfly 配置中定义两个 undertow 服务器就足够了,每个服务器都有自己的虚拟主机和 http 侦听器,如下所示:

    <server name="foo-server">
       <http-listener name="foo-listener" proxy-address-forwarding="true" socket-binding="foo-http"/>
       <host name="foo-host" default-web-module="foo.war" alias="localhost, foo.com, wwww.foo.com"/>
    </server>
    <server name="bar-server">
       <http-listener name="bar-listener" proxy-address-forwarding="true" socket-binding="bar-http"/>
       <host name="bar-host" default-web-module="bar.war" alias="localhost, bar.com, wwww.bar.com"/>
    </server>
    
    <socket-binding name="foo-http" port="${jboss.http.port:8080}"/>
    <socket-binding name="bar-http" port="${jboss.http.port:8181}"/>
    

    您的项目中还需要一个 jboss-web.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss-web>
       <server-instance>foo-server</server-instance>
       <virtual-host>foo-host</virtual-host>
       <context-root>/</context-root>
    </jboss-web>
    

    需要这两个服务器,因为您无法将套接字绑定添加到虚拟主机。所以这里有一点点开销,但与运行两个完整的应用程序服务器相比可以忽略不计。

    编辑 1:

    我突然想到,甚至可能不需要使用不同的端口,而且每个部署使用一个 undertow 服务器也可能是多余的。

    由于代理能够将客户端请求的主机转发到应用服务器,undertow 应该能够通过别名参数挑选出合适的虚拟主机。

    所以基本上,代理会将任何对 foo.com 或 bar.com 的请求转发到 localhost:8080,然后让 AS 解决问题。

    我没有对此进行测试,但它是如何工作的(同样,这是针对 Wildfly 10.0):

    <server name="default-server">
       <http-listener name="http" proxy-address-forwarding="true" socket-binding="http"/>
       <host name="foo-host" default-web-module="foo.war" alias="foo.com, wwww.foo.com"/>
       <host name="bar-host" default-web-module="bar.war" alias="bar.com, wwww.bar.com"/>
    </server>
    

    而 jboss-web.xml 会丢失服务器标签:

    <?xml version="1.0" encoding="UTF-8"?>
    <jboss-web>
       <virtual-host>foo-host</virtual-host>
       <context-root>/</context-root>
    </jboss-web>
    

    如果这可行,则根本不会涉及任何开销。

    编辑 2:

    刚刚测试了简化的方法 - 是的,它有效 :)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-03-31
      • 1970-01-01
      • 1970-01-01
      • 2023-01-26
      • 2017-10-02
      • 1970-01-01
      • 2011-04-18
      相关资源
      最近更新 更多