Spring MVC Static Resources partially work - spring-mvc

I have a basic directory app that works fine except that it seems to only sometimes find the static resources that I’ve configured using the mvc:resources tag. My search of the board found problems related to handler mappings, but my problem seems to be different.
Specifically, when the PersonController is called via a method mapping to “/person”, it returns personlist.jsp using the view resolver and correctly finds and uses the static css and js files. No problems.
When the same controller is called via another method mapping to “/person/{familyid}” ( narrows the person list to a particular family), it returns the same personlist.jsp…but now it fails to find or use the css and js files (though it does display the correct data).
I don’t understand why there is a behavior difference since both scenarios use the same Controller, the same return String (return “personlist”), and resolve to the same JSP (ie. with the same Head section links for the css, js).
I looked at what came back in the browser for each case using ‘view source’, and both pages return the same head tag rendering for the css and js linking:
<link href="resources/css/directory.css" rel="stylesheet" type="text/css"></link>
<script type="text/javascript" src="resources/scripts/jquery-1.7.min.js"></script>
<script type="text/javascript" src="resources/scripts/directory.js"></script>
I thought the problem could be with my tag mapping, so I also tried this:
<resources mapping="**/resources/**" and
resources mapping="resources/**"
but no help.
Am I approaching the use of static resources properly here (and what is the right resources tag mapping if that’s the problem)? Thanks.
I am using Spring 3.0.6 and my css and js files are located under /WebContent/resources/css and /WebContent/resources/scripts respectively, which are mapped using the mvc:resource tag (see below).
PersonController:
#Controller
public class PersonController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
List<Person> personList;
Boolean familyCalled = false;
#Autowired
PersonService personService;
#RequestMapping(value="/people", method=RequestMethod.GET)
public String people(Model model) {
logger.debug("Received request to show peoplelist page");
System.out.println("Running inside people() method of PersonController");
personList = personService.getPersons();
familyCalled = false;
model.addAttribute("personList", personList);
return "personlist";
}
#RequestMapping(value="/people/{familyId}", method=RequestMethod.GET)
public String familyMembers(#PathVariable("familyId") String fid, Model model) {
System.out.println("Running inside familyMembers() method of PersonController");
personList = personService.getPersonsInFamily(fid);
familyCalled = true;
model.addAttribute("personList", personList);
return "personlist";
}
Servlet-Context.xml (without namespaces):
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- for transactions -->
<tx:annotation-driven/>
<!-- Needed for #PreAuthorize security on methods -->
<aop:aspectj-autoproxy/>
<context:annotation-config />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="resources/" />
<!-- Enables the Spring MVC #Controller programming model -->
<annotation-driven/>
<!-- Resolves views selected for rendering by #Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.c3works.preps" />
</beans:beans>
personlist.jsp (Head section):
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
</meta>
<title>XXXXXXXXXXXXXXXXXXX</title>
<link href="resources/css/directory.css" rel="stylesheet" type="text/css">
</link>
<script type="text/javascript" src="resources/scripts/jquery-1.7.min.js">
</script>
<script type="text/javascript" src="resources/scripts/directory.js">
</script>
</head>

Your URLs are relative and therefore the browser is looking for the resources in the wrong place. (Check the resulting HTML code)
One solution is to use the <spring:url> tag to build the urls of the resources.
<spring:url var="banner" value="/resources/images/banner-graphic.png" />
<img src="${banner}" />

Related

Error while moving HTML form from jsp to ThymeLeaf

I am trying to follow this as reference Serving Static content in SpringBoot
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.ui.Model;
/**
* Created by Eric on 11/25/2015.
*/
#org.springframework.stereotype.Controller
public class Controller {
#RequestMapping("/appPage")
public String greeting(#RequestParam(value="name", required=false, defaultValue="World") String name, Model model) {
model.addAttribute("name", name);
model.addAttribute("title", "Best Of the App");
model.addAttribute("basecontext", "Best Of the App");
return "appPage";
}
}
My HTML form being below
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:text="${title}" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</head>
<body>
<p th:text="'Hello, ' + ${name} + '!'" />
<input type="hidden" type="text" id="basecontext" value='${basecontext}'/>
</body>
</html>
I am trying to set value in hidden input field. But that gives me error
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Exception parsing document: template="appPage", line 9 - column 33
I am trying to move this html page which was getting loaded in a JSP application to Spring Boot + ThymeLeaf.
If I simply place this content in index.html without a Context handler in Controller. the page is loaded just fine. Thymeleaf does not throw any error.
ThymeLeaf uses xml and not html and you are not allowed to have attributes of the same name in xml type="hidden" type="text"
You actually should get SAXParseException: Attribute "type" was already specified for element in your spring log

sec:authorize and sec:authentication annotations don't work

I have a Spring + Thymeleaf project with the following view code.
<!DOCTYPE html SYSTEM "http://www.thymeleaf.org/dtd/xhtml1-strict-thymeleaf-spring3-3.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<head>
<title>Contacts</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
</head>
<body>
<div id="content">
<h1>Welcome to the site!</h1>
<p th:if="${loginError}">Wrong user or password</p>
<form th:action="#{/j_spring_security_check}" method="post">
<label for="j_username">Email address</label>:
<input type="text" id="j_username" name="j_username"/> <br/>
<label for="j_password">Password</label>:
<input type="password" id="j_password" name="j_password"/> <br/>
<input type="submit" value="Log in"/>
</form>
</div>
<div sec:authorize="isAuthenticated()">
User: <span sec:authentication="name">miquel</span>
</div>
</body>
</html>
The sec:authorize and sec:authentication attributes don't work as expected - the div is always shown, even if no user is logged in, and the span always reads "miquel".
Follows a relevant snippet from my controller class.
#RequestMapping(value = "/welcome.html")
public String wellcome() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
System.out.println("username: " + auth.getName());
return "home";
}
The println statement works as expected - if no user is logged in, it prints "anonymousUser", otherwise the username.
What am I doing wrong?
After comparing my application closely to the Thymeleaf & Spring Security demo applicaiton, I discovered the source of the error.
Apparently, in order for Thymeleaf to process the sec:authorize and sec:authentication attributes, you need to register SpringSecurityDialect as an additional dialect of the template engine bean.
<bean id="templateEngine" class="org.thymeleaf.spring3.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
<property name="additionalDialects">
<set>
<bean class="org.thymeleaf.extras.springsecurity3.dialect.SpringSecurityDialect" />
</set>
</property>
</bean>
This is surprising as there is no mention of that fact on the related Thymeleaf documentation page. I hope this helps others who will face the same issue in future.
In Spring Boot I just had to add the following dependency:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
For the java config version, it worked for me too by adding the spring security dialect:
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(templateResolver());
templateEngine.addDialect(new TilesDialect());
templateEngine.addDialect(new SpringSecurityDialect());
return templateEngine;
}
Also, you may wish to clear the template cache after an authentication event, so your template is re-processed with new authentication data. Or, set the templates which are sensitive to a login session to non-cached (this is what I did), using ServletContextTemplateResolver.setNonCacheablePatterns().

HTTP Status 404 - Servlet .... is not available

I use eclise to create a servlet like this :
package hello;
public class NewServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("doPost");
String name = request.getParameter("textField");
response.setContentType("text/html");
PrintWriter pw = response.getWriter();
pw.print("<html><head></head><body><center>");
pw.print("Hello " + name + "!");
pw.print("</center></body></html>");
}
}
and a html file like :
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Insert title here</title>
</head>
<body>
<form method="post" action="NewServlet">
<p align="center">
<font>Input some text</font> <br> <input type="text"
name="textFiled"> <br> <input type="submit"
value="submit"> <br>
</p>
</form>
</body>
</html>
when i run the servlet, met an error :
HTTP Status 404 - Servlet NewServlet is not available
--------------------------------------------------------------------------------
type Status report
message Servlet NewServlet is not available
description The requested resource (Servlet NewServlet is not available) is not available.
i checked the folder : WEB-INF or any folder else and can't see file .class
How is this caused and how can I solve it?
You should check web-inf folder in your IDE and map your servlet in web.xml file
<servlet>
<servlet-name>NewServlet</servlet-name>
<servlet-class>NewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>NewServlet</servlet-name>
<url-pattern>/NewServlet</url-pattern>
</servlet-mapping>
make sure that this mapping is done properly and also your servlet is not in any package or folder if so then in servlet tag write that class name followed by . and your servlet name.
If problem still arises just make sure that you delete that .class file of your servlet and build your project again.(Net beans have an option of clean and build and then run) haven't use eclipse but i am sure it has similar option as well
Servlets needs to be registered and mapped on a specific URL pattern in order to be able to execute them by a HTTP request. Given your HTML code you seem to expect the servlet to listen on an URL of /NewServlet.
If you're using Tomcat 7, then just put the #WebServlet annotation on the class with exactly that URL pattern:
#WebServlet("/NewServlet")
public class NewServlet extends HttpServlet {
// ...
}
If you're still on Tomcat 6 or older for some reason, then you'd need to do it by the old fashioned web.xml way. A concrete example can be found in our servlets wiki page.
Eclipse won't show .class files in the project explorer. It will only show them in the navigator, in the /build folder. But this should not be something to worry about right now.
Format in web.xml should be like this.
<servlet-name>NewServlet</servlet-name>
<servlet-class>PackageName.JavaClass</servlet-class>
which in your case is
<servlet-name>NewServlet</servlet-name>
<servlet-class>Hello.NewServlet</servlet-class>
edite this:
form method="post" action="NewServlet"
to this
form method="post" action="/(your project name ) /NewServlet"
i had the same problem and this worked for me

spring bean - inject Context Base URL in property

On Spring 3, I got a bean in my applicationContext.xml with this definition :
<bean id="serviceProperties" class="org.springframework.security.cas.ServiceProperties">
<property name="service" value="http://www.mysite.com/my_webapp/j_spring_cas_security_check"/>
<property name="sendRenew" value="false"/>
</bean>
Instead of
"value="http://www.mysite.com/my_webapp/j_spring_cas_security_check"
Is there a way to directly and dynamically inject the "base URL" of my tomcat server + j_spring_cas_security_check
( like we can do in a jsp to set :
<base href="<%=request.getScheme() + "://" + request.getServerName()+ ":" + request.getServerPort() + request.getContextPath()+ "/"%>" />
)
Try this:
<%# taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<spring:url value="/j_spring_cas_security_check" var="baseUrl" />
<base href="${baseUrl}" />
spring:url evaluates the URL "/j_spring_cas_security_check" relative to your application path and stores it in baseUrl variable. After this, you can replace ${baseUrl} in your JSP.
It's recommended not to use base tag. Instead, you should have relative paths but this is up to you.
Hope it helps.

Root route breaking paths

I'm in the process of deploying an ASP.NET MVC app to IIS 6, but am running into an issue with the root path.
In Global.asax, I have the root path mapped:
routes.MapRoute("Root", "",
new { controller = "Dashboard", action = "Index", id = "" });
When I navigate to http://servername:70/test2/, the app displays the right page, but the stylesheet and JavaScript files are not loading. Looking at the source, the paths are showing like this:
<script src="test2/Scripts/MicrosoftAjax.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css"
href="test2/Content/stylesheets/app.css" />
Which makes the browser look for
http://servername:70/test2/test2/Content/stylesheets/app.css
When I go directly to a controller (http://servername:70/test2/Dashboard.aspx), the paths are correct:
<link rel="stylesheet" type="text/css" href="Content/stylesheets/app.css" />
This is also occurring with any links generated with ActionLink. The stylesheet and script paths are being generated with Url.Content:
<link rel="stylesheet" type="text/css"
href="<%= Url.Content("~/Content/stylesheets/app.css") %>" />
I have recently answered a question similar to this which uses Rob Conery's Script Registration Helper. I'll copy the answer in here for you, and add an example HtmlHelper for stylesheets.
public static string RegisterJS(this System.Web.Mvc.HtmlHelper helper, string scriptLib) {
//get the directory where the scripts are
string scriptRoot = VirtualPathUtility.ToAbsolute("~/Scripts");
string scriptFormat="<script src=\"{0}/{1}\" type=\"text/javascript\"></script>\r\n";
return string.Format(scriptFormat,scriptRoot,scriptLib);
}
public static string RegisterCSS(this System.Web.Mvc.HtmlHelper helper, string styleLink, string rel) {
//get the directory where the css is
string stylesheetRoot = VirtualPathUtility.ToAbsolute("~/Content/Stylesheets");
string styleFormat="<link type='text/css' href='{0}/{1}' rel='{1}' />\r\n";
return string.Format(styleFormat, stylesheetRoot, styleLink, rel);
}
Usage:
<%= Html.RegisterJS("myscriptFile.js") %>
<%= Html.RegisterCSS("app.css") %>
Hope this helps.
Also, I note that there was another response on that question by Levi:
This should have been fixed in RC2. If you are using RC2 and are still seeing this problem, please file a bug at http://forums.asp.net/1146.aspx.
If this is your preferred answer, the please upvote Levi's response.
Or, use the Url.Content ...
<link href="<%=Url.Content("~/Content/Site.css") %>" rel="stylesheet" type="text/css" />

Resources