在Web应用程序中处理上下文的任何聪明方法?

时间:2008-09-24 04:02:20

标签: java jsp servlets contextpath

在Java中,Web应用程序捆绑在WAR中。默认情况下,许多servlet容器将使用WAR名称作为应用程序的上下文名称。

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

问题是webapp认为它的“root”是“root”,或者只是“/”,而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(没有特别优雅)。

对于编码人员来说,跳出何时使用“App Relative”网址与绝对网址是一个很小的问题。

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

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

你做什么?

14 个答案:

答案 0 :(得分:24)

您可以使用JSTL创建网址。

例如,<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结合使用,所涉及的技术是相似的 - 唯一真正的区别在于以不同的方式获取上下文根。

答案 1 :(得分:8)

对于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中,只要您想将相对URL转换为绝对URL,就可以从DOM访问<base>元素。

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

或者如果你做jQuery

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

在CSS中,图像URL相对于样式表本身的URL。因此,只需将图像放在相对于样式表本身的某个文件夹中。 E.g。

/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');

另见:

答案 2 :(得分:4)

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

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

有助于将* / resources路径的所有请求转发到/ resources pass(包括上下文路径前缀)。因此,我可以将所有图片 CSS 文件放在资源文件夹下,并继续在我的样式中使用相对URL作为背景图片和其他案例。

答案 3 :(得分:3)

  

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你的路径自动翻译为包含上下文路径。

答案 4 :(得分:1)

我已经使用帮助程序类来生成img标记等。这个帮助程序类使用应用程序的contextPath处理前缀路径。 (这很有效,但我真的不喜欢它。如果有人有更好的选择,请告诉我。)

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

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

答案 5 :(得分:1)

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

“flat”我的意思是你的应用程序根目录下没有子目录,也许只有几个静态内容目录为“images /”。您的所有JSP,操作URL,servlet都直接位于根目录下。

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

答案 6 :(得分:1)

Vilmantas在这里说的是正确的词:相对的URL。

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

<img src="myimage.gif"/>

而不是

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

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

答案 7 :(得分:1)

除特殊情况外,我建议不要以这种方式使用绝对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保存在同一个平面文件路径空间中。也就是说,/myapp/之后每个网址都不应有斜杠。

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

有特殊情况。例如如果您在Javascript中编写浏览器端应用程序,那么维护平坦的文件路径空间会变得更加困难。在这种情况下,或者在其他特殊情况下,或者只是在您有个人偏好的情况下,使用<%= request.getContextPath() %>来创建绝对路径并不是一件大事。

答案 8 :(得分:1)

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

这不适用于CSS图像替换,除非您想动态生成CSS文件,这可能会导致其他问题。但是,既然您知道CSS文件与图像的关系,那么您可以使用相对URL。

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

答案 9 :(得分:1)

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

我认为问题的关键(和共识)是拥有一个可能有多个目录的网站。

如果您的目录深度(缺少更好的术语)是不变的,那么您可以依赖CSS之类的相对URL。

介意,布局不必完全平坦,只是一致。

例如,我们已经完成了/ css,/ js,/ common,/ admin,/ user等层次结构。将适当的页面和资源放在适当的目录中。拥有这样的结构可以很好地处理基于容器的身份验证。

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

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

答案 10 :(得分:0)

我通过表示声称以下是一个优雅的问题。事实上,事后看来,鉴于(最有可能)性能影响,我不会推荐这个问题。

我们的网络应用程序的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}来检索上下文路径?那时它对我们来说很好。

答案 11 :(得分:0)

从头开始创建网站时,我会与@Will一起 - 寻求一致且可预测的网址结构,以便您可以坚持使用相对引用。

但是,如果您要更新最初构建为直接在站点根目录“/”(简单的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

答案 12 :(得分:0)

我倾向于将一个属性作为我的核心JavaScript库的一部分。我认为这不是完美的,但我认为这是我能够实现的最佳目标。

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

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

然后我使用带有通用标头的apache磁贴,它总是包含以下内容:

getUrl(path)

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

请注意,以下两者都是有意的。 var path = APP.getUrl("/some/path"); var path2 = APP.getUrl("some/path"); 将始终返回绝对路径,因为相对路径不需要您首先了解上下文。

require './handy_dandy_helper'
include HandyDandyHelper

答案 13 :(得分:0)

这是最好的方法: 上下文重定向过滤器 必须在匹配前的扩展点应用过滤器,因此使用注释@PreMatching。

实现此接口的过滤器必须使用@Provider进行注释,以便由JAX-RS运行时发现。还可以发现容器请求过滤器实例并将其动态绑定到特定资源方法。

用示例代码解释:

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