【问题标题】:Any clever ways of handling the context in a web app?在 Web 应用程序中处理上下文的任何巧妙方法?
【发布时间】:2010-09-12 15:02:19
【问题描述】:

在 Java 中,Web 应用程序被捆绑到 WAR 中。默认情况下,许多 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 标签来做到这一点。如果您执行 您可能会得到 404,因为您真正想要的是“/myapp/myimage.gif”。

许多框架也具有上下文感知的 JSP 标签,并且在 JSP 中创建正确 URL 的方法有多种(没有一种特别优雅)。

对于编码人员来说,跳出何时使用“应用程序相对”网址而不是绝对网址是一个棘手的问题。

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

我很好奇其他人使用什么技术来缓解和解决这个问题。许多人只是简单地对它进行平底船和硬编码,无论是服务器根目录还是他们碰巧使用的任何上下文。我已经知道那个答案了,这不是我要找的。​​p>

你是做什么的?

【问题讨论】:

    标签: java jsp servlets contextpath


    【解决方案1】:

    您可以使用 JSTL 来创建 url。

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

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

    我有一个这样的网络根结构:

    /css
    /图片

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

    至于 JavaScript,对我有用的是在页眉中包含一些常见的 JavaScript,如下所示:

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

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

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

    【讨论】:

    • 你知道不使用JSP的方法吗?能够在 pom.xml 或类似文件中设置应用程序名称会很好。
    【解决方案2】:

    对于 HTML 页面,我只是设置了 HTML &lt;base&gt; 标签。每个相对链接(即不是以 scheme 或 / 开头)都将变得相对于它。 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 中,只要您想将相对 URL 转换为绝对 URL,就可以从 DOM 访问 &lt;base&gt; 元素。

    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】:

      我同意延迟。 我也关注过滤器,并在UrlRewriteFilter 项目面前找到了解决方案。 简单配置如下:

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

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

      【讨论】:

        【解决方案4】:

        Servlet API 和 JSP 具有 设施来帮助管理这一点。为了 例如,如果在 servlet 中执行以下操作: response.sendRedirect("/mypage.jsp"), 容器将预先设置上下文 并创建网址: http://example.com/myapp/mypage.jsp"。

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

        来自Servlet 2.3: New features exposed

        最后,经过长时间的辩论 专家组,Servlet API 2.3 已经一劳永逸地澄清了 发生了什么 res.sendRedirect("/index.html") 调用 对于在 a 中执行的 servlet 非根上下文。问题是 Servlet API 2.2 需要一个不完整的 像“/index.html”这样的路径 由 servlet 容器翻译 成一个完整的路径,但没有说 如何处理上下文路径。如果 发出调用的 servlet 位于 路径“/contextpath”处的上下文 重定向 URI 是否应该翻译 相对于容器根 (http://server:port/index.html) 或 上下文根 (http://server:port/contextpath/index.html)? 为了获得最大的便携性,它是 必须定义行为; 经过长时间的辩论,专家们 选择相对于 容器根。对于那些想要 上下文相关,您可以在前面加上 从 getContextPath() 输出到您的 URI。

        所以不,在 2.3 中,您的路径不会自动翻译为包含上下文路径。

        【讨论】:

          【解决方案5】:

          我已经使用helper classes 来生成img 标签等。这个helper 类负责前缀路径 与应用程序的contextPath。 (这行得通,但我不是很喜欢。如果有人有更好的选择,请告诉。)

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

          或者,我有时会使用 Ant 脚本,该脚本用不同环境的适当数据替换 @token@ 标记。在这种情况下,@contextPAth@ 标记将被替换为正确的上下文路径。

          【讨论】:

            【解决方案6】:

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

            我所说的“平面”是指在您的应用程序根目录下没有子目录,可能只有几个用于静态内容的目录,例如“images/”。您所有的 JSP、操作 URL、servlet 都直接位于根目录下。

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

            【讨论】:

            • 如果您在子目录中,即 /ContextRoot/subdir/page.xhtml,这将不起作用。我发现总是最好用相对于上下文根的路径作为前缀,这样你的页面就可以在任何地方工作
            【解决方案7】:

            Vilmantas 在这里说对了:相对 URL。

            您在 IMG 中需要做的就是使用

            <img src="myimage.gif"/>
            

            而不是

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

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

            【讨论】:

            • 虽然这是真的,但它也与现有路径相关。你可能会得到类似
            【解决方案8】:

            除特殊情况外,我建议不要以这种方式使用绝对 URL。曾经。当另一个 webapp 指向您的 webapp 中的某些内容时,绝对 URL 非常有用。在内部——当一个资源指向同一上下文中的第二个资源时——该资源应该知道它所在的位置,因此它应该能够表达到第二个资源的相对路径。

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

            /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 编写浏览器端应用程序,那么维护平面文件路径空间会变得更加困难。在那种情况下,或者在其他特殊情况下,或者只是你有个人偏好,使用&lt;%= request.getContextPath() %&gt; 创建绝对路径并不是什么大问题。

            【讨论】:

              【解决方案9】:

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

              这不适用于 CSS 图像替换,除非您想动态生成 CSS 文件,否则可能会导致其他问题。但是,由于您知道 CSS 文件与图像相关的位置,因此您可以摆脱相对 URL。

              由于某种原因,我在 IE 处理相对 URL 时遇到了麻烦,不得不回退到使用将 JavaScript 变量设置为上下文的表达式。我只是将我的 IE 图像替换分割成它们自己的文件,并使用 IE 宏来提取正确的图像。这没什么大不了的,因为无论如何我已经不得不这样做来处理透明的 PNG。它不漂亮,但它有效。

              【讨论】:

                【解决方案10】:

                我已经使用了大部分这些技术(除了 XSLT 架构)。

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

                如果您的目录深度(因为没有更好的术语)是恒定的,那么您可以在 CSS 等内容中依赖相对 url。

                请注意,布局不必完全平坦,只要保持一致即可。

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

                我还将 *.css 和 *.js 映射到 JSP servlet,并使它们动态化,以便我可以即时构建它们。

                我只是希望还有一些我可能错过的东西。

                【讨论】:

                  【解决方案11】:

                  I by no 表示声称以下是一个优雅的问题。事实上,事后看来,考虑到(最有可能的)性能下降,我不会推荐这个问题。

                  我们的网络应用程序的 JSP 是严格的 XML 原始数据。然后将这些原始数据发送到应用正确 CSS 标记的 XSL(服务器端),并输出 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】:

                    从头开始创建网站时,我支持@Will - 目标是一致且可预测的 url 结构,以便您可以坚持使用相对引用。

                    但是,如果您将最初构建为直接在站点根“/”(简单 JSP 站点非常常见)下工作的站点更新为正式的 Java EE 打包(其中上下文根将是一些根目录下的路径)。

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

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

                    此处提供示例站点和可用代码:EnforceContextRootFilter-1.0-src.zip 注意:实际的映射规则在 servlet 类中实现为正则表达式,并提供了一个非常通用的包罗万象 - 但您可能需要针对特定​​情况进行修改。

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

                    【讨论】:

                      【解决方案13】:

                      我倾向于将属性编写为我的核心 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 瓦片,该标题始终包含以下内容:

                      <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>
                      

                      现在我已经初始化了上下文,我可以在任何地方(js 文件或 jsp/html 中)使用getUrl(path),它将返回上下文中给定输入字符串的绝对路径。

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

                      var path = APP.getUrl("/some/path");
                      var path2 = APP.getUrl("some/path");
                      

                      【讨论】:

                        猜你喜欢
                        • 1970-01-01
                        • 2011-03-20
                        • 2016-06-03
                        • 1970-01-01
                        • 2011-09-26
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2019-11-27
                        相关资源
                        最近更新 更多