在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(用于背景图像等)。
我很好奇其他人使用什么技术来缓解和解决这个问题。许多人只是简单地将其编码并硬编码,无论是服务器根目录还是他们碰巧使用的任何上下文。我已经知道答案了,这不是我想要的。
你做什么?
答案 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运行时发现。还可以发现容器请求过滤器实例并将其动态绑定到特定资源方法。
用示例代码解释: