代码之家  ›  专栏  ›  技术社区  ›  Will Hartung

有什么巧妙的方法来处理Web应用程序中的上下文吗?

  •  39
  • Will Hartung  · 技术社区  · 16 年前

    在爪哇,网络应用程序被捆绑在战争中。默认情况下,许多servlet容器将使用war名称作为应用程序的上下文名称。

    因此myapp.war被部署到 http://example.com/myapp .

    问题是webapp认为它的“根”是“根”,或者仅仅是“/”,而html则认为你的应用的根是“/myapp”。

    servlet api和jsp具有帮助管理这一点的功能。例如,如果在servlet中执行以下操作:response.sendRedirect(“/mypage.jsp”),则容器将预先处理上下文并创建URL: http://example.com/myapp/mypage.jsp “。

    但是,不能用HTML中的img标记来实现这一点。如果您执行<img src=“/myimage.gif”/>操作,可能会得到404,因为您真正想要的是/myapp/myimage.gif”。

    许多框架也有上下文感知的JSP标记,并且在JSP中有不同的方法来生成正确的URL(没有一个特别优雅)。

    对于编码人员来说,从何时开始使用“应用程序相对”的URL,而不是绝对的URL,这是一个棘手的问题。

    最后,还有需要动态创建URL的JavaScript代码问题,以及CSS中嵌入的URL(用于背景图像等)。

    我很好奇其他人使用什么技术来缓解和解决这个问题。许多人只是简单地将其punt并硬编码,要么是服务器根目录,要么是它们碰巧使用的任何上下文。我已经知道答案了,这不是我想要的。

    你是做什么的?

    14 回复  |  直到 7 年前
        1
  •  24
  •   user7094    16 年前

    可以使用JSTL创建URL。

    例如, <c:url value="/images/header.jpg" /> 将作为上下文根的前缀。

    对于CSS,这通常对我来说不是问题。

    我有这样一个web根结构:

    CSS
    图像

    在css文件中,您只需要使用相对的URL(../images/header.jpg),而不需要知道上下文根目录。

    至于javascript,对我有用的是在页面标题中包含一些常见的javascript,如下所示:

    <script type="text/javascript">
    var CONTEXT_ROOT = '<%= request.getContextPath() %>';
    </script>
    

    然后,您可以在所有脚本中使用上下文根(或者,您可以定义一个函数来构建路径——可能会更灵活)。

    显然,这一切都取决于您使用JSP和JSTL,但我将JSF与Facelets结合使用,所涉及的技术是相似的——唯一真正的区别是以不同的方式获取上下文根。

        2
  •  8
  •   Community CDub    7 年前

    对于HTML页面,我只是设置HTML <base> 标签。每个相关链接(即不从方案或 / )将成为与之相关的。没有干净的方法可以马上抓住它 HttpServletRequest ,所以我们在这里不需要JSTL的帮助。

    <%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
    <%@taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions" %>
    <c:set var="req" value="${pageContext.request}" />
    <c:set var="url">${req.requestURL}</c:set>
    <c:set var="uri">${req.requestURI}</c:set>
    
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <base href="${fn:substring(url, 0, fn:length(url) - fn:length(uri))}${req.contextPath}/" />
        <link rel="stylesheet" href="css/default.css">
        <script src="js/default.js"></script>
      </head>
      <body>
        <img src="img/logo.png" />
        <a href="other.jsp">link</a>
      </body>
    </html>
    

    然而,这反过来又有一个警告:锚 #identifier url)也将成为相对于基本路径的。如果您有它们中的任何一个,那么您希望使其与请求URL(URI)相对。所以,像改变

    <a href="#identifier">jump</a>
    

    <a href="${uri}#identifier">jump</a>
    

    在JS中,您只需访问 <基地& GT; 每当您想将相对URL转换为绝对URL时,都可以从DOM中获取元素。

    var base = document.getElementsByTagName("base")[0].href;
    

    或者如果你做jquery

    var base = $("base").attr("href");
    

    在CSS中,图像URL是相对于样式表本身的URL的。所以,只需将图像放到与样式表本身相关的文件夹中。例如。

    /css/style.css
    /css/images/foo.png
    

    并参考如下

    background-image: url('images/foo.png');
    

    如果您希望将图像放到与CSS文件夹相同级别的文件夹中

    /css/style.css
    /images/foo.png
    

    然后使用 ../ 转到公用父文件夹

    background-image: url('../images/foo.png');
    

    参见:

        3
  •  4
  •   Demwis    14 年前

    我同意 焦油状的 . 我也注意过过滤器,并在面对项目时找到了解决方案。 UrlRewriteFilter . 简单配置如下:

    <rule>
        <from>^.+/resources/(.*)$</from>
        <to>/resources/$1</to>
    </rule>
    

    帮助将*/resources路径的所有请求转发到/resources pass(包括上下文路径前缀)。所以我可以简单地把我所有的 图像 CSS 文件下 资源 文件夹并继续在我的样式中使用背景图像和其他情况下的相对URL。

        4
  •  3
  •   tardate    16 年前

    servlet API和JSP 帮助管理这一点的设施。为了 例如,如果在servlet中,您执行以下操作: response.sendRedirect(“/mypage.jsp”), 容器将预先处理上下文 并创建URL: http://example.com/myapp/mypage.jsp “。

    啊,也许,也许不是-这取决于您的容器和servlet规范!

    Servlet 2.3: New features exposed :

    最后,经过长时间的辩论 一组专家,servlet API 2.3 彻底澄清了 A上发生了什么? res.sendRedirect(“/index.html”)调用 对于在 非根上下文。问题是 servlet api 2.2要求不完整 路径类似于/index.html 由servlet容器翻译 一条完整的路,但没有说 如何处理上下文路径。如果 发出呼叫的servlet位于 路径“/context path”处的上下文, 重定向URI是否应转换 相对于容器根 ( http://server:port/index.html 或 上下文根 ( http://server:port/contextpath/index.html )? 为了最大程度的可移植性, 必须定义行为; 经过长时间的辩论,专家们 选择相对于 容器根。为那些想要 上下文相关,您可以在 从getContextPath()输出到 URI。

    所以不,2.3你的路径是 自动转换为包含上下文路径。

        5
  •  1
  •   kosoant    16 年前

    我已经用过 助手类 生成img标记等。此助手类负责 前缀路径 使用应用程序的ContextPath。(这行,但我不太喜欢。如果有更好的选择,请告知。)

    对于CSS文件等中的路径,我使用 Ant构建脚本 在生产环境中使用site.production.css for site.css,在开发环境中使用site.development.css。

    或者我有时会使用一个Ant脚本 替换@token@ 具有不同环境的适当数据的令牌。在这种情况下,@context path@token将替换为正确的上下文路径。

        6
  •  1
  •   Vilmantas Baranauskas    16 年前

    一种选择是尽可能使用“平面”应用程序结构和相对URL。

    “flat”的意思是在应用程序根目录下没有子目录,静态内容的目录可能只有“images/”。所有JSP、操作URL和servlet都直接位于根目录下。

    这并不能完全解决您的问题,但可以大大简化它。

        7
  •  1
  •   Scott Stanchfield    16 年前

    维尔曼塔在这里说得对:相对网址。

    在你的图像处理中你需要做的就是使用

    <img src="myimage.gif"/>
    

    而不是

    <img src="/myimage.gif"/>
    

    它将与应用程序上下文相关(因为浏览器正在解释要转到的URL)

        8
  •  1
  •   Travis Wilson    16 年前

    除了特殊情况,我建议不要用这种方式使用绝对URL。曾经。绝对URL适用于 另一个网络应用程序 正在指向您的webapp中的某个内容。在内部——当一个资源指向同一上下文中的第二个资源时——该资源应该知道它的位置,所以它应该能够表示到第二个资源的相对路径。

    当然,您将编写模块化组件,这些组件不知道包含它们的资源。例如:

    /myapp/user/email.jsp:
    Email: <a href="../sendmail.jsp">${user.email}</a>
    
    /myapp/browse/profile.jsp:
    <jsp:include page="../user/email.jsp" />
    
    /myapp/home.jsp:
    <jsp:include page="../user/email.jsp" />
    

    那么,如何 email.jsp 知道…的相对路径 sendmail.jsp ?很明显,链接将在 /myapp/browse/profile.jsp 否则它会破裂 /myapp/home.jsp . 答案是,将所有URL保持在同一平面文件路径空间中。也就是说,每个URL后面都不应该有斜杠 /myapp/ .

    只要在URL和生成内容的实际文件之间有某种映射,就很容易实现这一点。(例如,在Spring中,使用DispatcherServlet将URL映射到JSP文件或视图。)

    有特殊情况。例如,如果您正在用JavaScript编写浏览器端的应用程序,那么很难维护一个平面文件路径空间。在这种情况下,或者在其他特殊情况下,或者只是在你有个人偏好的情况下,使用它并不是什么大问题。 <%= request.getContextPath() %> 创建绝对路径。

        9
  •  1
  •   Brian Deterling    16 年前

    您可以使用request.getContextPath()来构建绝对URL,这些URL不是硬编码到特定上下文的。如前所述,对于JavaScript,您只需在JSP的顶部设置一个变量(或者最好在模板中设置),并将其作为上下文前缀。

    这不适用于CSS图像替换,除非您希望动态生成一个CSS文件,这可能会导致其他问题。但是,由于您知道您的CSS文件与您的图像的关系,所以您可以使用相对的URL。

    出于某种原因,我在处理相关的URL时遇到了麻烦,不得不退回到使用具有设置为上下文的javascript变量的表达式。我只是将我的IE图像替换部分拆分成他们自己的文件,并使用IE宏来插入正确的文件。这没什么大不了的,因为不管怎样,我已经不得不这么做来处理透明PNG了。它不漂亮,但很管用。

        10
  •  1
  •   Will Hartung    16 年前

    我已经使用了大多数这些技术(保存了XSLT架构)。

    我认为问题的症结(和共识)在于拥有一个可能包含多个目录的站点。

    如果您的目录深度(缺少更好的术语)是常量,那么您可以在CSS之类的东西中依赖相对URL。

    记住,布局不必完全平坦,只要一致。

    例如,我们已经完成了/css、/js、/common、/admin、/user等层次结构。将适当的页面和资源放在适当的目录中。这样的结构对于基于容器的身份验证非常有效。

    我还将*.css和*.js映射到JSP servlet,并使它们成为动态的,这样我就可以动态地构建它们。

    我只是希望我可能错过了一些别的事情。

        11
  •  0
  •   Swati Markus    16 年前

    我通过 意味着声称以下是一个优雅的问题。事实上,事后看来,考虑到(最有可能的)性能受到影响,我不会推荐这个问题。

    我们的Web应用的JSP是严格的XML原始数据。然后将这些原始数据发送到一个XSL(服务器端),该XSL应用了正确的CSS标记,并输出XHTML。

    我们有一个template.xsl,它将由我们为网站的不同组件所拥有的多个xsl文件继承。我们的路径都是在名为paths.xml的XSL文件中定义的:

    <?xml version="1.0" encoding="UTF-8"?>
    <paths>
        <path name="account" parent="home">Account/</path>
        <path name="css">css/</path>
        <path name="home">servlet/</path>
        <path name="icons" parent="images">icons/</path>
        <path name="images">images/</path>
        <path name="js">js/</path>
    </paths>
    

    XML中的内部链接如下:

    <ilink name="link to icons" type="icons">link to icons</ilink>
    

    这将由我们的XSL处理:

    <xsl:template match="ilink">
        <xsl:variable name="temp">
            <xsl:value-of select="$rootpath" />
            <xsl:call-template name="paths">
                <xsl:with-param name="path-name"><xsl:value-of select="@type" /></xsl:with-param>
            </xsl:call-template>
            <xsl:value-of select="@file" />
        </xsl:variable>
            <a href="{$temp}" title="{@name}" ><xsl:value-of select="." /></a>
    </xsl:template>
    

    $rootPath 传递到每个文件 ${applicationScope.contextPath} 我们使用XML而不是仅仅在JSP/Java文件中硬编码的想法是我们不想重新编译的。

    同样,这个解决方案根本不是一个好的解决方案……但我们确实使用过一次!

    编辑 :实际上,问题的复杂性是因为我们不能在整个视图中使用JSP。为什么没人用 $applicationScope.contextPath_ 要检索上下文路径吗?那对我们来说很管用。

        12
  •  0
  •   Community CDub    7 年前

    当从头开始创建一个站点时,我站在@will的一边——目标是一个一致的、可预测的URL结构,这样您就可以坚持使用相对引用。

    但是,如果您将一个最初构建为直接在站点根目录“/”(对于简单的JSP站点来说很常见)下工作的站点更新为正式的,那么事情可能会变得非常混乱。 Java EE 打包(其中上下文根将是根下的某个路径)。

    这可能意味着大量的代码更改。

    如果您希望避免或推迟代码更改,但仍然要确保上下文根引用正确,那么我测试的一种技术是使用servlet过滤器。可以将筛选器放到现有项目中而不更改任何内容(web.xml除外),并将出站HTML中的任何URL引用重新映射到正确的路径,同时确保正确引用重定向。

    示例站点和可用代码如下: EnforceContextRootFilter-1.0-src.zip 注意:实际的映射规则在servlet类中作为regex实现,并提供了一个非常通用的catch all——但是您可能需要根据特定情况进行修改。

    顺便说一下,我提出了一个稍微不同的问题 migrating existing code base from "/" to a non-root context-path

        13
  •  0
  •   Brett Ryan    8 年前

    我倾向于编写一个属性作为我的核心JavaScript库的一部分。我不认为这是完美的,但我认为这是我尽力做到的最好的。

    首先,我有一个模块,它是我的应用程序核心的一部分,始终可用

    (function (APP) {
      var ctx;
      APP.setContext = function (val) {
        // protect rogue JS from setting the context.
        if (ctx) {
          return;
        }
        val = val || val.trim();
        // Don't allow a double slash for a context.
        if (val.charAt(0) === '/' && val.charAt(1) === '/') {
          return;
        }
        // Context must both start and end in /.
        if (val.length === 0 || val === '/') {
          val = '/';
        } else {
          if (val.charAt(0) !== '/') {
            val = '/' + val;
          }
          if (val.slice(-1) !== '/') {
            val += '/';
          }
        }
        ctx = val;
      };
      APP.getContext = function () {
        return ctx || '/';
      };
      APP.getUrl = function (val) {
        if (val && val.length > 0) {
          return APP.getContext() + (val.charAt(0) === '/' ? val.substring(1) : val);
        }
        return APP.getContext();
      };
    })(window.APP = window.APP || {});
    

    然后,我使用带有公共头的Apache Tiles,该头始终包含以下内容:

    <script type="text/javascript">
      APP.setContext('${pageContext.request['contextPath']}');
      // If preferred use JSTL cor, but it must be available and declared.
      //APP.setContext('<c:url value='/'/>');
    </script>
    

    既然我已经初始化了我可以使用的上下文 getUrl(path) 从任何位置(JS文件或JSP/HTML中)返回上下文中给定输入字符串的绝对路径。

    请注意,以下内容都是有意等效的。 getUrl 将始终返回绝对路径,因为相对路径不需要您首先了解上下文。

    var path = APP.getUrl("/some/path");
    var path2 = APP.getUrl("some/path");
    
        14
  •  0
  •   Bindu S    7 年前

    最好的方法是: 上下文重定向筛选器 必须在预匹配扩展点应用筛选器,因此使用annotation@prematching。

    实现此接口的过滤器必须用@provider注释,以便JAX-RS运行时发现。容器请求筛选器实例也可以被发现并动态绑定到特定的资源方法。

    用示例代码解释:

    http://writeulearn.com/java-filters/