Browser can't access/find relative resources like CSS, images and links when calling a Servlet which forwards to a JSP
Asked Answered
P

10

87

I'm having trouble with loading CSS and images and creating links to other pages when I have a servlet forward to a JSP. Specifically, when I set my <welcome-file> to index.jsp, the CSS is being loaded and my images are being displayed. However, if I set my <welcome-file> to HomeServlet which forwards control to index.jsp, the CSS is not being applied and my images are not being displayed.

My CSS file is in web/styles/default.css.
My images are in web/images/.

I'm linking to my CSS like so:

<link href="styles/default.css" rel="stylesheet" type="text/css" />

I'm displaying my images as follows:

<img src="images/image1.png" alt="Image1" />

How is this problem caused and how can I solve it?


Update 1: I've added the structure of the application, as well as some other information that might help.

alt text

The header.jsp file is the file that contains the link tag for the CSS. The HomeServlet is set as my welcome-file in web.xml:

<welcome-file-list>
    <welcome-file>HomeServlet</welcome-file>
</welcome-file-list>

The servlet is declared and mapped as followes in the web.xml:

<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>

Update 2: I found the problem finally - my servlet was mapped incorrectly. Apparently when setting a Servlet as your <welcome-file> it can't have a URL pattern of /, which I find sort of weird, because wouldn't that stand for the root directory of the site?

The new mapping is as follows:

<servlet-mapping>
    <servlet-name>HomeServlet</servlet-name>
    <url-pattern>/HomeServlet</url-pattern>
</servlet-mapping>
Phanerozoic answered 7/9, 2010 at 2:9 Comment(0)
B
106

All relative URLs in the HTML page generated by the JSP file are relative to the current request URL (the URL as you see in the browser address bar) and not to the location of the JSP file in the server side as you seem to expect. It's namely the webbrowser who has to download those resources individually by URL, not the webserver who has to include them from disk somehow.

Apart from changing the relative URLs to make them relative to the URL of the servlet instead of the location of the JSP file, another way to fix this problem is to make them relative to the domain root (i.e. start with a /). This way you don't need to worry about changing the relative paths once again when you change the URL of the servlet.

<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>

However, you would probably like not to hardcode the context path. Very reasonable. You can obtain the context path in EL by ${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 can easily be shortened by <c:set var="root" value="${pageContext.request.contextPath}" /> and used as ${root} elsewhere)

Or, if you don't fear unreadable XML and broken XML syntax highlighting, use 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>

Either way, this is in turn pretty cumbersome if you have a lot of relative URLs. For that you can use the <base> tag. All relative URL's will instantly become relative to it. It has however to start with the scheme (http://, https://, etc). There's no neat way to obtain the base context path in plain EL, so we need a little help of JSTL here.

<%@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>

This has in turn (again) some caveats. Anchors (the #identifier URL's) will become relative to the base path as well! You would like to make it relative to the request URL (URI) instead. So, change like

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

to

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

Each way has its own pros and cons. It's up to you which to choose. At least, you should now understand how this problem is caused and how to solve it :)

See also:

Bulkhead answered 7/9, 2010 at 12:41 Comment(9)
I was really hoping that this would work as it makes sense, but the browser still can't locate the files. I've added some more information to my question (app structure, and some other general info). Thanks for the help!Phanerozoic
You've mapped the servlet on / (smell, smell ;) ). So it also intercepts on CSS files (every HTTP request in fact). Is it handling them correctly? I.e. you are able to access the CSS file directly in webbrowser by localhost:8080/context/styles/default.css ?Bulkhead
Not when it was mapped to "/". I can when it is mapped as "/HomeServlet" though.Phanerozoic
I am not sure why you initially mapped it on /*, but if you're trying to create kind of a front controller, I'd suggest to get yourself through this answer and maybe also this answer.Bulkhead
@Bulkhead - In your first paragraph, where you said "It's namely the webbrowser who has to download those resources individually by URL, not the webserver who has to include them from disk somehow." Can we check it practically that it's the browser's responsibility to fetch the resources and not the webserver? If you can point me to some practical example or tutorial that will be great!Decameter
@AbhishekAggarwal: F12 in webbrowser and check Network tab.Bulkhead
Hello @BalusC, would it be possible that you take a look at my latest thread which is something similar but in a remote server? stackoverflow.com/questions/73062830/…Zimmermann
@Bulkhead Maybe it's just me, but when you say "You can obtain the context path in EL by [...]", what does "EL" stand for?Hydromel
@user1438038: stackoverflow.com/tags/el/infoBulkhead
C
6

I faced similar issue with Spring MVC application. I used < mvc:resources > tag to resolve this issue.

Please find the following link having more details.

http://www.mkyong.com/spring-mvc/spring-mvc-how-to-include-js-or-css-files-in-a-jsp-page/

Correction answered 21/11, 2014 at 21:56 Comment(0)
C
2

You must analyse the actual HTML output, for the hint.

By giving the path like this means "from current location", on the other hand if you start with a / that would mean "from the context".

Cholinesterase answered 7/9, 2010 at 2:57 Comment(5)
I'm unsure of what you mean. If I change the css link to this:Phanerozoic
I mean you can view the HTML source from your browser, to find out if the path is right or not. If it is not, what should you need to do in order to make it correct.Cholinesterase
I've been viewing the source, and what comes up for my link tags is what I posted in my question. I guess I'm unsure of why if I link directly to my jsp, that the files can be found, and if I do a forward, they can't be found? Where can I store my images so they can be found from any location in the web app?Phanerozoic
Okay, it is because your index.jsp is on the same location/level as your styles and images directories are. Therefore, when you directly use index.jsp as a welcome file, everything appears like charm. On the other hand, when you forward the same resource through the servlet, the matter is not the same anymore. [to be continued...]Cholinesterase
@Brian D.: ...To direct a request to a particular servlet we don't consider the path, we use servlet mapping. Now, we must understand the context path here. As you have seen in the docs that / has some specific meaning when used in the path we want to forward the request, or redirect the request. Without the slash it will be taken from the current location, not from the context path. I hope you are getting me now.Cholinesterase
W
1

You can try out this one as well as. Because this worked for me and it's simple.

<style>
    <%@ include file="/css/style.css" %>
</style>
Wernsman answered 26/4, 2020 at 2:35 Comment(0)
A
0

Your welcome page is set as That Servlet . So all css , images path should be given relative to that servlet DIR . which is a bad idea ! why do you need the servlet as a home page ? set .jsp as index page and redirect to any page from there ?

are you trying to populate any fields from db is that why you are using servlet ?

Acroter answered 7/9, 2010 at 4:56 Comment(1)
Using a servlet as (MVC) front controller is certainly not a bad idea.Bulkhead
M
0

If you are using Spring MVC, then you need to declare default action servlet for static contents. Add the following entries in spring-action-servlet.xml. It worked for me.

NOTE: keep all the static contents outside 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"/>
Matusow answered 1/7, 2014 at 6:54 Comment(0)
G
0

As for your update, I was confused for the reasoning behind in. Dug a little deeper and found this gem:

  • yoursite.com becomes yoursite.com/
  • yoursite.com/ is a directory, so the welcome-file-list is scanned
  • yoursite.com/CMS is the first welcome-file ("CMS" in the welcome-file-list), and there is a mapping of /CMS to the MyCMS servlet, so that servlet is accessed.

Source: http://wiki.metawerx.net/wiki/HowToUseAServletAsYourMainWebPage

So, the mapping then does make sense.

And one can now freely use ${pageContext.request.contextPath}/path/ as src/href for relative links!

Gonadotropin answered 23/11, 2014 at 9:6 Comment(0)
L
0

short answer - add following line in the jsp which will define the base
base href="/{root of your application}/"

Lacework answered 19/3, 2017 at 7:13 Comment(0)
C
0

Below code worked for me.

instead of use <%@ include file="styles/default.css"%>

Consignee answered 29/7, 2018 at 7:8 Comment(0)
N
-1

Simple no-brainer

Directory Structure:

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>

For putting inside the servlet controller

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();

Notate answered 9/4, 2023 at 7:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.