有什么聪明的方法来处理Web应用程序中的上下文?

时间:2020-03-06 14:38:30  来源:igfitidea点击:

在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创建网址。

例如,&lt;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编写浏览器端应用程序,则很难维护平面文件路径空间。在那种情况下,或者在其他特殊情况下,或者只是我们有个人喜好,使用&lt;%= 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中,路径不会自动转换为包括上下文路径。