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

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

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

问题是,webapp认为它的“根”是“根”,或者简单地说是“/”,而HTML会认为你的应用程序的根是“/ myapp”。

Servlet API和JSP有帮助pipe理的function。 例如,如果在servlet中执行:response.sendRedirect(“/ mypage.jsp”),则容器将预先添加上下文并创buildurl: http : //example.com/myapp/mypage.jsp 。

但是,你不能用HTML中的IMG标签来做到这一点。 如果你使用<img src =“/ myimage.gif”/>你可能会得到一个404,因为你真正想要的是“/myapp/myimage.gif”。

许多框架都有JSP标签,它们也是上下文感知的,在JSP中有不同的方法来创build正确的URL(没有特别的优雅)。

对于编码人员来说,跳出何时使用“应用相对”url与绝对url是一个问题。

最后,Javascript代码的问题需要dynamic地创buildURL,并在CSS内embeddedURL(用于背景图像等)。

我很好奇别人用什么技术来缓解和解决这个问题。 许多人只是踢和硬编码,无论是服务器的根或任何上下文,他们碰巧正在使用。 我已经知道了答案,那不是我要找的。

你是做什么?

您可以使用JSTL来创buildurl。

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

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

我有这样的networking根结构:

/ CSS
/图片

在CSS文件中,您只需要使用相对URL(..http://img.dovov.comheader.jpg),而不需要知道上下文根。;

至于JavaScript,对我来说最合适的是在页面头部包含一些常见的JavaScript,如下所示:

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

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

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

对于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)也将相对于基础path。 如果你有其中的任何一个,你可以改为相对于请求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相关。 所以,只需要将图像放在相对于样式表本身的某个文件夹中。 例如

 /css/style.css /csshttp://img.dovov.comfoo.png 

并参考如下

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

如果您想将图像放在与CSS文件夹相同的某个文件夹中

 /css/style.css http://img.dovov.comfoo.png 

然后使用../转到共同的父文件夹

 background-image: url('..http://img.dovov.comfoo.png'); 

也可以看看:

  • build议使用<base> html标签吗?

Servlet API和JSP有帮助pipe理的function。 例如,如果在servlet中执行:response.sendRedirect(“/ mypage.jsp”),则容器将预先添加上下文并创buildurl: http : //example.com/myapp/mypage.jsp 。

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

从Servlet 2.3开始:暴露的新function :

最后,经过一组专家的长时间辩论之后,Servlet API 2.3已经彻底澄清了res.sendRedirect(“/ index.html”)调用servlet在非根环境中执行的情况。 问题在于Servlet API 2.2需要一个不完整的path,比如“/index.html”被servlet容器翻译成一个完整的path,但是并没有说明如何处理上下文path。 如果进行调用的servlet位于path“/ contextpath”的上下文中,则redirectURI应该相对于容器根( http:// server:port / index.html )或上下文根( http:// server:port / contextpath / index.html )? 为了最大限度的便携性,定义行为是必要的。 经过漫长的辩论,专家们select了相对于容器根的翻译。 对于那些想要上下文相关的用户,可以将getContextPath()的输出前置到URI中。

所以不,2.3,你的path不会自动翻译成包含上下文path。

我同意迟到 。 我也注意到了filter,并在项目UrlRewriteFilter中find了解决scheme。 简单的configuration如下:

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

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

我使用助手类来生成img标签等。这个助手类负责与应用程序的contextPath path前缀 。 (这有用,但我不太喜欢,如果有人有更好的select,请告诉。)

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

或者,我有时使用一个Ant脚本, 不同的environents的正确数据replace@ token @ tokens。 在这种情况下,@ contextPAth @ token将被replace为正确的上下文path。

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

通过“平坦”我的意思是没有你的应用程序根目录下的子目录,可能只是静态内容作为“图像/”几个目录。 您的所有JSP,操作URL,servlet直接位于根目录下。

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

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

所有你需要在你的IMG做的是使用

 <img src="myimage.gif"/> 

代替

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

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

除了特殊情况,我build议不要使用绝对URL这种方式。 永远。 当另一个web应用程序指向web应用程序中的某些内容时,绝对URL很有用。 在内部 – 当一个资源指向同一个上下文中的第二个资源时 – 资源应该知道它在哪里,所以它应该能够expression到第二个资源的相对path。

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

 /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的相对path呢? 很显然,链接将在/myapp/browse/profile.jsp上断开,或者在/myapp/home.jsp/myapp/home.jsp 。 答案是,将所有URL保存在同一个平面文件path空间中。 也就是说,每个URL在/myapp/之后都不应该有斜杠。

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

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

您可以使用request.getContextPath()构build绝对URL,这些URL不是硬编码到特定的上下文。 正如前面的答案所指出的那样,对于JavaScript,您只需在JSP顶部(或者最好在模板中)设置一个variables,并将前缀作为上下文。

这不适用于CSS图像replace,除非你想dynamic生成一个CSS文件,这可能会导致其他问题。 但是既然你知道你的CSS文件与你的图像有关,你可以逃脱相对的URL。

出于某种原因,我在处理相对URL的IE中遇到了麻烦,不得不退回到使用带有设置为上下文的JavaScriptvariables的expression式。 我只是将我的IE图像replace分离到他们自己的文件,并使用IEmacros来拉入正确的。 这不是什么大问题,因为我已经必须这样做来处理透明的PNG了。 这不是很漂亮,但它的工作原理。

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

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

如果你的目录深度(因为没有一个更好的术语)是不变的,那么你可以依靠像CSS这样的相关URL。

介意,布局不一定要完全平坦,一致。

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

我还将* .css和* .js映射到JSP servlet,并使它们变成dynamic的,以便我可以即时创build它们。

我只是希望有一些我可能错过了。

绝不认为以下是一个优雅的问题。 事实上,事后看来,我不会推荐这个问题(最有可能)的performance。

我们的Web应用程序的JSP是严格的XML原始数据。 这个原始数据然后被发送到应用了正确的CSS标签的XSL(服务器端),并吐出XHTML。

我们有一个template.xsl,可以通过我们为网站的不同组件获得的多个XSL文件inheritance。 我们的path都是在一个名为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> 

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

再一次,解决scheme不是一个好的…但我们曾经使用它一次!

编辑 :事实上,我们问题的复杂性是因为我们无法使用JSP来实现整个视图。 为什么不会有人使用${applicationScope.contextPath}来检索上下文path? 那对我们来说工作得很好。

当从头开始创build网站时,我会使用@Will的方式 – 旨在获得一致且可预测的url结构,以便您可以坚持使用相关参考。

但是如果你正在更新一个最初构build的,直接在站点根目录“/”下工作的站点(对于简单的JSP站点来说很常见),正式的Java EE打包(其中上下文根将是根)。

这可能意味着很多代码更改。

如果你想避免或推迟代码更改,但仍然确保正确的上下文根引用,我已经testing的技术是使用servletfilter。 这个filter可以被放到一个已经存在的项目中而不会改变任何东西(web.xml除外),并且会把出站HTML中的URL参考重新映射到正确的path,并且确保redirect被正确地引用。

一个示例站点和可用的代码在这里可用: EnforceContextRootFilter-1.0-src.zip注意:实际的映射规则在servlet类中被实现为正则expression式,并且提供了一个相当普遍的全部 – 但是你可能需要修改特定的情况。

顺便说一句,我分叉了一个稍微不同的问题来解决现有的代码从“/”迁移到非根上下文path

我倾向于写一个属性作为我的核心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) ),它将返回上下文中给定inputstring的绝对path。

请注意以下两点都是故意的。 getUrl将始终返回一个绝对path,因为相对path不需要您首先知道上下文。

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