浏览器在调用转发到JSP的Servlet时无法访问/找到相对资源,如CSS、图片和链接

86
我在将servlet转发到JSP时,遇到了加载CSS、图片和创建到其他页面链接的问题。具体来说,当我将<welcome-file>设置为index.jsp时,CSS被加载并且我的图片也被显示出来。但是,如果我将<welcome-file>设置为HomeServlet,然后再将控制转发到index.jsp时,CSS不会被应用并且我的图片不会被显示出来。
我的CSS文件位于web/styles/default.css
我的图片位于web/images/
以下是我的CSS链接方式:
<link href="styles/default.css" rel="stylesheet" type="text/css" />
我将我的图片显示如下:
<img src="images/image1.png" alt="Image1" />
这个问题是如何引起的,我该如何解决?

更新1:我添加了应用程序的结构以及其他可能有帮助的信息。

alt text

header.jsp文件包含CSS的链接标签。在web.xml中将HomeServlet设置为我的welcome-file

<welcome-file-list>
    <welcome-file>HomeServlet</welcome-file>
</welcome-file-list>
MyServlet com.example.MyServlet MyServlet /myservlet/*
web.xml中声明和映射servlet如下所示:
<servlet>
    <servlet-name>HomeServlet</servlet-name>
    <servlet-class>com.brianblog.frontend.HomeServlet</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>HomeServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

更新2:我最终找到了问题所在 - 我的Servlet映射设置错误。显然,当将一个Servlet设置为<welcome-file>时,它不能具有URL模式/,这让我感到有点奇怪,因为它不是代表站点的根目录吗?

新的映射如下:

<servlet-mapping>
    <servlet-name>HomeServlet</servlet-name>
    <url-pattern>/HomeServlet</url-pattern>
</servlet-mapping>
10个回答

105

由JSP文件生成的HTML页面中的所有相对URL都是相对于当前请求URL(即在浏览器地址栏中看到的URL),而不是相对于服务器端JSP文件位置,这一点可能与您的期望不同。实际上,需要通过URL单独下载这些资源的是Web浏览器,而不是从磁盘中将它们包含到Web服务器中。

除了更改相对URL以使它们相对于Servlet的URL而不是JSP文件位置之外,另一种解决此问题的方法是使它们相对于域根(即以/开头)。这样,当您更改Servlet的URL时,就无需担心再次更改相对路径。

<head>
    <link rel="stylesheet" href="/context/css/default.css" />
    <script src="/context/js/default.js"></script>
</head>
<body>
    <img src="/context/img/logo.png" />
    <a href="/context/page.jsp">link</a>
    <form action="/context/servlet"><input type="submit" /></form>
</body>

然而,您可能不想硬编码上下文路径。这是非常合理的。您可以通过EL获取上下文路径:${pageContext.request.contextPath}

<head>
    <link rel="stylesheet" href="${pageContext.request.contextPath}/css/default.css" />
    <script src="${pageContext.request.contextPath}/js/default.js"></script>
</head>
<body>
    <img src="${pageContext.request.contextPath}/img/logo.png" />
    <a href="${pageContext.request.contextPath}/page.jsp">link</a>
    <form action="${pageContext.request.contextPath}/servlet"><input type="submit" /></form>
</body>

(which可以通过<c:set var="root" value="${pageContext.request.contextPath}" />轻松缩短,并在其他地方使用${root})

或者,如果你不害怕无法阅读的XML和损坏的XML语法高亮,请使用JSTL<c:url>:

<head>
    <link rel="stylesheet" href="<c:url value="/css/default.css" />" />
    <script src="<c:url value="/js/default.js" />"></script>
</head>
<body>
    <img src="<c:url value="/img/logo.png" />" />
    <a href="<c:url value="/page.jsp" />">link</a>
    <form action="<c:url value="/servlet" />"><input type="submit" /></form>
</body>

无论如何,如果您有很多相对URL,这会变得非常麻烦。为此,您可以使用<base>标签。所有相对URL将立即变成相对于它的URL。但是,它必须以方案(http://https://等)开头。在普通EL中没有简洁的方法来获取基本上下文路径,因此我们需要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="uri" value="${req.requestURI}" />
<c:set var="url">${req.requestURL}</c:set>
...
<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="page.jsp">link</a>
    <form action="servlet"><input type="submit" /></form>
</body>

这反过来(再次)有一些注意事项。锚点(#identifier URL)也将相对于基本路径!您希望它相对于请求URL(URI)而不是基本路径。因此,请进行更改,如下:

<a href="#identifier">jump</a>

为了

<a href="${uri}#identifier">jump</a>

每种方法都有其优缺点。选择哪种方法取决于您。至少,现在您应该了解如何引起此问题以及如何解决它 :)

另请参阅:


3
你已将Servlet映射到“/”(有点儿玄乎)。因此,它也会拦截CSS文件(事实上是每个HTTP请求)。它是否正确处理它们?也就是说,你能够通过http://localhost:8080/context/styles/default.css 直接在Web浏览器中访问CSS文件吗? - BalusC
我不确定为什么您最初将其映射到 /*,但如果您正在尝试创建一种前置控制器,我建议您阅读这个答案,也许还有这个答案 - BalusC
你好@BalusC,您能否看一下我的最新帖子,它与远程服务器上的某些类似吗?https://stackoverflow.com/questions/73062830/java-jstl-cimport-url-from-another-server-and-resolve-relative-paths - xBurnsed
@BalusC 或许只有我这样想,但是当你说“您可以通过[...]在EL中获取上下文路径”时,EL代表什么? - user1438038
1
@user1438038:https://stackoverflow.com/tags/el/info - BalusC
显示剩余4条评论

6

2

为了获得提示,您必须分析实际的HTML输出。

如果像这样给出路径意味着“从当前位置开始”,另一方面,如果以/开头,则意味着“从上下文中开始”。


我不确定你的意思。如果我将CSS链接更改为这个: - Brian DiCasa
我的意思是您可以从浏览器中查看HTML源代码,以确定路径是否正确。如果不正确,您应该采取什么措施使其正确。 - Adeel Ansari
我一直在查看源代码,我的链接标签显示的是我在问题中发布的内容。我猜我不确定的是,为什么如果我直接链接到我的jsp,文件可以被找到,但如果我进行转发,它们就无法被找到?我应该把我的图片存储在哪里,以便从Web应用程序的任何位置都可以找到它们? - Brian DiCasa
好的,这是因为您的 index.jsp 与您的 stylesimages 目录位于同一位置/级别上。因此,当您直接使用 index.jsp 作为欢迎文件时,一切都像魅力一样。另一方面,当您通过servlet转发相同的资源时,情况就不再相同了。[待续...] - Adeel Ansari
@Brian D.:...为了将请求定向到特定的servlet,我们不考虑路径,而是使用servlet映射。现在,我们必须理解这里的“上下文路径”。正如您在文档中看到的那样,当在我们想要转发或重定向请求的路径中使用“/”时,它具有某些特定的含义。如果没有斜杠,则会从当前位置获取,而不是从上下文路径获取。我希望你现在明白我的意思了。 - Adeel Ansari

0
如果您正在使用Spring MVC,则需要为静态内容声明默认的操作servlet。在spring-action-servlet.xml中添加以下条目。这对我有用。
注意:将所有静态内容保留在WEB-INF之外。

<!-- Enable annotation-based controllers using @Controller annotations -->
<bean id="annotationUrlMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">
    <property name="order" value="0" />
</bean>

<bean id="controllerClassNameHandlerMapping" class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping">
    <property name="order" value="1" />
</bean>

<bean id="annotationMethodHandlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>

0
以下代码对我有效。
使用以下代码代替原本的代码:
<%@ include file="styles/default.css"%>

0
关于您的更新,我对其背后的原因感到困惑。深入挖掘之后,我发现以下的重点:
  • yoursite.com 成为了 yoursite.com/
  • yoursite.com/ 是一个目录,所以 welcome-file-list 被扫描了
  • yoursite.com/CMS 是第一个欢迎文件(welcome-file-list 中的“CMS”),并且有一个映射将 /CMS 映射到 MyCMS servlet,所以该 servlet 被访问。

来源:http://wiki.metawerx.net/wiki/HowToUseAServletAsYourMainWebPage

所以,这个映射现在是有意义的。

现在,您可以自由地使用 ${pageContext.request.contextPath}/path/ 作为相对链接的 src/href!


0

简短回答 - 在 JSP 中添加以下行,它将定义基础
base href="/{您的应用程序根目录}/"


0

您的欢迎页面被设置为该Servlet。因此,所有CSS、图像路径都应相对于该Servlet目录给出。这是一个不好的想法!为什么需要将Servlet设置为主页?将.jsp设置为索引页面,并从那里重定向到任何页面?

您是否尝试从数据库中填充任何字段,这就是为什么要使用Servlet的原因吗?


4
使用servlet作为(MVC)前端控制器绝对不是一个坏主意。 - BalusC

0
你也可以试试这个。因为这对我来说很有效而且简单。
<style>
    <%@ include file="/css/style.css" %>
</style>

-1

简单易懂

目录结构:

webapp/
  css/
    bootstrap.min.css
index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
<link rel='stylesheet' type='text/css' href='css/bootstrap.min.css'>
</head>
<body>
<h4>Testing JSP Form</h4>

<form action="firstServlet" method="post">
<label for="firstname"> First Name</label><input type="text" name="firstname"> <br>
<input type="submit" value="Submit" >
</form>

</body>
</html>

用于放置在Servlet控制器内部

writer.println("<!DOCTYPE html>\n"
                + "<html lang=\"en\">\n"
                + "<head>\n"
                + "<meta charset=\"UTF-8\">\n"
                + "<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n"
                + "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n"
                + "<link rel=\'stylesheet\' type=\'text/css\' href=\'css/bootstrap.min.css\'>\n"
                + "<title>Document</title>\n"
                + "</head>\n"
                + "<body>\n"
                + "<h3>Hello " + firstName + "</h3>"
                + "</body>\n"
                + "</html>\n");
        writer.close();


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接