有什么聪明的方法来处理Web应用程序中的上下文?
在Java中,Web应用程序捆绑在WAR中。默认情况下,许多servlet容器将WAR名称用作应用程序的上下文名称。
因此,myapp.war被部署到http://example.com/myapp。
问题在于,Web应用程序将其"根"视为"根",或者仅仅是" /",而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" URL与绝对URL相比是一个小问题。
最后,存在Javascript代码的问题,该Javascript代码需要动态创建URL,并在CSS内嵌入URL(用于背景图像等)。
我很好奇其他人使用了哪些技术来缓解和解决此问题。许多服务器只是简单地对其进行修补和硬编码,无论是在服务器根目录还是在它们恰巧使用的任何上下文中。我已经知道答案了,这不是我想要的。
你做什么工作?
解决方案
我绝不声称以下是一个优雅的问题。实际上,事后看来,鉴于(最有可能的)性能下降,我不建议使用此问题。
我们的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}来检索上下文路径?那时对我们来说还不错。
我已经使用了帮助程序类来生成img标记等。该帮助程序类负责使用应用程序的contextPath前缀路径。 (这可行,但是我不太喜欢。如果有人有更好的选择,请告诉。)
对于css文件等中的路径,我使用了一个Ant生成脚本,该脚本在生产环境中的site.css使用site.production.css,在开发环境中使用site.development.css。
另外,有时我会使用一个Ant脚本,用适用于不同环境的适当数据替换@ token @令牌。在这种情况下,@ contextPAth @标记将被替换为正确的上下文路径。
一种选择是尽可能使用"平面"应用程序结构和相对URL。
"扁平"是指应用程序根目录下没有子目录,静态目录中可能只有很少的目录作为" images /"。我们所有的JSP,操作URL和servlet都直接位于根目录下。
这不能完全解决问题,但可以大大简化它。
我们可以使用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一起使用,并且所涉及的技术相似,唯一的真正区别是以不同的方式获取上下文根。
Vilmantas在这里说了正确的词:相对URL。
我们在IMG中要做的就是使用
<img src="myimage.gif"/>
代替
<img src="/myimage.gif"/>
并且它是相对于应用程序上下文的(因为浏览器正在解释要访问的URL)
除特殊情况外,建议不要以这种方式使用绝对URL。曾经。当另一个Web应用程序指向Web应用程序中的某个内容时,绝对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编写浏览器端应用程序,则很难维护平面文件路径空间。在那种情况下,或者在其他特殊情况下,或者只是我们有个人喜好,使用<%= request.getContextPath()%>
创建绝对路径并不是什么大问题。
我们可以使用request.getContextPath()来构建未硬编码到特定上下文的绝对URL。如前所述,对于JavaScript,我们只需在JSP的顶部(最好是在模板中)设置一个变量,并将其作为上下文前缀。
除非我们要动态生成CSS文件,否则这不适用于CSS图像替换,这可能会导致其他问题。但是,由于我们知道CSS文件相对于图片的位置,因此可以避免使用相对URL。
出于某种原因,我在IE处理相对URL时遇到了麻烦,不得不回退使用将JavaScript变量设置为上下文的表达式。我只是将我的IE映像替换文件拆分成自己的文件,并使用IE宏提取正确的文件。没什么大不了的,因为无论如何我都必须这样做以处理透明的PNG。它不漂亮,但是可以用。
我已经使用了大多数这些技术(保存XSLT体系结构)。
我认为问题的症结(和共识)是拥有一个可能包含多个目录的网站。
如果目录深度(由于缺乏更好的术语)是恒定的,则可以在CSS之类的内容中依赖相对URL。
请注意,布局不必完全平坦,只需保持一致即可。
例如,我们已经完成了/ css,/ js,/ common,/ admin,/ user等层次结构。将适当的页面和资源放在适当的目录中。具有这样的结构非常适合基于容器的身份验证。
我还将* .css和* .js映射到JSP servlet,并使它们成为动态的,以便我可以动态地构建它们。
我只是希望还有其他我可能错过的事情。
从头开始创建网站时,我支持@Will的目标是保持一致且可预测的url结构,以便我们可以坚持使用相对引用。
但是,如果将最初构建为直接在站点根目录" /"下工作的站点(对于简单的JSP站点而言很常见)更新为正式的Java EE打包(上下文根目录是根目录下的某些路径),事情可能会变得非常混乱)。
那可能意味着很多代码更改。
如果要避免或者推迟代码更改,但仍要确保正确的上下文根引用,我测试过的一种技术是使用servlet过滤器。可以将过滤器放到现有的项目中,而无需进行任何更改(web.xml除外),并且可以将出站HTML中的所有url引用重新映射到正确的路径,并确保正确引用了重定向。
此处提供示例站点和可用代码:EnforceContextRootFilter-1.0-src.zip
注意:实际的映射规则在servlet类中作为正则表达式实现,并提供了相当通用的功能,但是我们可能需要针对特定情况进行修改。
顺便说一句,我提出了一个稍微不同的问题,以解决将现有代码库从" /"迁移到非根上下文路径的问题。
The Servlet API and JSP have facilities to help manage this. For example, if, in a servlet, you do: response.sendRedirect("/mypage.jsp"), the container will prepend the context and create the url: http://example.com/myapp/mypage.jsp".
啊,也许吧,也许不取决于容器和servlet规范!
从Servlet 2.3:公开了新功能:
And finally, after a lengthy debate by a group of experts, Servlet API 2.3 has clarified once and for all exactly what happens on a res.sendRedirect("/index.html") call for a servlet executing within a non-root context. The issue is that Servlet API 2.2 requires an incomplete path like "/index.html" to be translated by the servlet container into a complete path, but doesn't say how context paths are handled. If the servlet making the call is in a context at the path "/contextpath," should the redirect URI translate relative to the container root (http://server:port/index.html) or the context root (http://server:port/contextpath/index.html)? For maximum portability, it's imperative to define the behavior; after lengthy debate, the experts chose to translate relative to the container root. For those who want context relative, you can prepend the output from getContextPath() to your URI.
所以不能,在2.3中,路径不会自动转换为包括上下文路径。