您收到404 File Not Found
错误的原因是您作为href
属性值给出的 CSS 的路径缺少上下文路径。
HTTP 请求 URL 包含以下部分:
http://[host]:[port][request-path]?[query-string]
请求路径进一步由以下元素组成:
上下文路径:正斜杠 (/) 与servlet 的 Web 应用程序的上下文根的串联。例子:http://host[:port]/context-root[/url-pattern]
Servlet path:对应于激活此请求的组件别名的路径部分。此路径以正斜杠 (/) 开头。
路径信息:请求路径中不属于上下文路径或 servlet 路径的部分。
在这里阅读更多。
解决方案
您的问题有多种解决方案,以下是其中一些:
1) 使用<c:url>
来自 JSTL 的标签
在我的 Java Web 应用程序中,我通常在定义 CSS/JavaScript/image 和其他静态资源的路径时使用来自JSTL<c:url>
的标记。通过这样做,您可以确保始终相对于应用程序上下文(上下文路径)引用这些资源。
如果您说您的 CSS 位于WebContent文件夹中,那么这应该可以工作:
<link type="text/css" rel="stylesheet" href="<c:url value="/globalCSS.css" />" />
它工作的原因在“ JavaServer Pages™ 标准标记库”版本 1.2规范第 7.5 章(强调我的)中进行了解释:
7.5 <c:url>
建立一个应用了正确重写规则的 URL。
...
URL 必须是以方案开头的绝对 URL(例如“http://server/context/page.jsp”)或 JSP 1.2 在 JSP.2.2.1“相对 URL 规范”中定义的相对 URL ”。因此,实现必须将上下文路径添加到以斜杠开头的 URL(例如“/page2.jsp”),以便客户端浏览器可以正确解释此类 URL。
注意
不要忘记在您的 JSP 中使用Taglib 指令来引用JSTL 标记。另请参阅此处的示例 JSP 页面。
2)使用JSP表达式语言和隐式对象
另一种解决方案是使用表达式语言 (EL)添加应用程序上下文:
<link type="text/css" rel="stylesheet" href="${pageContext.request.contextPath}/globalCSS.css" />
在这里,我们从请求对象中检索了上下文路径。为了访问请求对象,我们使用了pageContext 隐式对象。
3) 使用<c:set>
来自 JSTL 的标签
免责声明
这个解决方案的想法取自这里。
要使访问上下文路径比在解决方案 №2 中更紧凑,您可以首先使用 JSTL<c:set>
标记,该标记在任何 JSP 范围(页面、请求、会话)中设置 EL 变量的值或 EL 变量的属性或应用程序)供以后访问。
<c:set var="root" value="${pageContext.request.contextPath}"/>
...
<link type="text/css" rel="stylesheet" href="${root}/globalCSS.css" />
重要说明
默认情况下,为了以这种方式设置变量,包含此set标记的 JSP 必须至少访问一次(包括在使用范围属性设置应用程序范围内的值的情况下,如),然后再使用此新变量。例如,您可以有多个需要此变量的 JSP 文件。因此,您必须 a)在应用程序范围内设置新变量保存上下文路径并首先访问此 JSP,然后在其他 JSP 文件中使用此变量,或者 b) 在您需要的每个 JSP 文件中设置此上下文路径保存变量访问它。<c:set var="foo" value="bar" scope="application" />
4) 使用 ServletContextListener
使访问上下文路径更紧凑的更有效方法是设置一个变量,该变量将保存上下文路径并使用Listener将其存储在应用程序范围内。这个解决方案类似于解决方案№3,但好处是现在变量保存上下文路径是在 Web 应用程序的开始处设置的,并且在应用程序范围内可用,不需要额外的步骤。
我们需要一个实现ServletContextListener接口的类。这是此类的示例:
package com.example.listener;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
@WebListener
public class AppContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent event) {
ServletContext sc = event.getServletContext();
sc.setAttribute("ctx", sc.getContextPath());
}
@Override
public void contextDestroyed(ServletContextEvent event) {}
}
现在在 JSP 中,我们可以使用 EL 访问这个全局变量:
<link type="text/css" rel="stylesheet" href="${ctx}/globalCSS.css" />
注意
@WebListener注释从 Servlet 3.0 版开始可用。如果您使用支持旧 Servlet 规范的 servlet 容器或应用程序服务器,请删除@WebServlet注释并改为在部署描述符(web.xml) 中配置侦听器。以下是支持最大 Servlet 版本 2.5 的容器的web.xml文件示例(为简洁起见,省略了其他配置):
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
...
<listener>
<listener-class>com.example.listener.AppContextListener</listener-class>
</listener>
...
</webapp>
5) 使用小脚本
正如用户@gavenkoa所建议的,您还可以像这样使用scriptlet:
<%= request.getContextPath() %>
对于这么小的事情,它可能是可以的,请注意,通常不鼓励在 JSP 中使用 scriptlet。
结论
我个人更喜欢第一个解决方案(大部分时间在我以前的项目中使用它)或第二个,因为它们最清晰、直观和明确(恕我直言)。但是你选择最适合你的。
其他想法
您可以将您的 Web 应用程序部署为默认应用程序(即在默认根上下文中),因此无需指定上下文路径即可访问它。有关更多信息,请阅读此处的“更新”部分。