spring boot /WEB-INF doesn't work - java

#SpringBootApplication
#Controller
public class App {
#RequestMapping("/aaa")
String home(HttpServletRequest request) {
return "nihao";
}
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/res/");
resolver.setSuffix(".html");
return resolver;
}
}
This code works fine. The path of 'nihao.html' is in '/static/res/nihao.html'. But when I change the prefix to "/WEB-INF/", and create /WEB-INF/nihao.html file, I've got a 404 no message available error. and I've got the same error when I change the prefix to to /static/WEB-INF/" and create requisite file in that directory. I need someone to help me figure out. All the path is relative to the context path.

Related

Spring Boot Thymeleaf 3 external javascript template configuration

I have a spring boot application which is working as an OAuth2 client.
I'm using Thymeleaf 3 as template engine. These are the Thymeleaf 3 related dependencies in my build.gradle.
compile('org.thymeleaf:thymeleaf:3.0.1.RELEASE')
compile('org.thymeleaf:thymeleaf-spring4:3.0.1.RELEASE')
compile('nz.net.ultraq.thymeleaf:thymeleaf-layout-dialect:2.0.4')
compile('org.thymeleaf.extras:thymeleaf-extras-springsecurity4:3.0.1.RELEASE')
It makes no difference if I reference the dependencies like this:
ext["thymeleaf.version"] = "3.0.2.RELEASE"
ext["thymeleaf-layout-dialect.version"] = "2.1.1"
dependencies {
compile('org.springframework.boot:spring-boot-starter-thymeleaf')
}
I want to be able to use Thymeleaf 3's HTML and Javascript template modes.
HTML mode works, but in javascript mode the messageSource is not working properly.
Here is my WebMvcConfiguration class:
#Configuration
public class WebMVCConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware {
private static final String CHARACTER_ENCODING = "UTF-8";
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#Bean
public ViewResolver htmlViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine(htmlTemplateResolver()));
resolver.setContentType("text/html");
resolver.setCharacterEncoding(CHARACTER_ENCODING);
resolver.setViewNames(new String[] {"*.html"});
return resolver;
}
#Bean
public ViewResolver javascriptViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine(javascriptTemplateResolver()));
resolver.setContentType("application/javascript");
resolver.setCharacterEncoding(CHARACTER_ENCODING);
resolver.setViewNames(new String[] {"*.js"});
return resolver;
}
private TemplateEngine templateEngine(ITemplateResolver templateResolver) {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setTemplateResolver(templateResolver);
return engine;
}
private ITemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("templates/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setSuffix(".html");
return resolver;
}
public ITemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:/static/js/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
// resolver.setSuffix(".js");
return resolver;
}
}
Please note that I had to use "classpath:/static/js/" in javascriptTemplateResolver, because when I used only "static/js/", I got the following exception:
java.io.FileNotFoundException: Could not open ServletContext resource [/static/js/headerconfig.js]
Also I had to comment out setSuffix, because with it I get the following exception:
java.io.FileNotFoundException: class path resource [static/js/typeutils.js.js] cannot be opened because it does not exist
I think this already indicates the main problem but I can't figure out what may cause it.
I have a controller for handling javascript templates:
#Controller
public class JavascriptController {
#RequestMapping(method = RequestMethod.GET, value = "/js/{template}.js")
public String jsMapping(#PathVariable("template") String template, Model model) {
model.addAttribute("myAttribute", "Attribute works!");
return template;
}
}
My messages.properties files are located in
src/main/resources/messages.properties
src/main/resources/messages_hu.properties
I have an HTML file in src/main/resources/templates/ folder, which references a javascript file like so:
<script th:src="#{js/typeutils.js}"></script>
The referenced javascript file (js/typeutils.js):
var a = [[${myAttribute}]];
var b = [[#{test}]];
console.log(a);
console.log(b);
When I run the application and check the javascript console, this is what's being printed out:
Attribute works!
??test_hu_HU??
So the model attribute was successfully passed to the javascript file, and the localization has been detected, but the message wasn't found for 'test'.
It seems as if the javascript template mode behaves completely different from HTML template mode.
How should if fix the javascript template mode config in order for it to process the messages.properties files, too?
Thanks!
I figured out the solution (by looking through thymeleaf sourcecode), which might not be perfect, but it works!
Unlike the example given by Baeldung, the following solved the problem for me:
#Configuration
public class WebMVCConfig implements WebMvcConfigurer, ApplicationContextAware {
private static final String CHARACTER_ENCODING = "UTF-8";
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
#Bean
public ViewResolver htmlViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setContentType("text/html");
resolver.setCharacterEncoding(CHARACTER_ENCODING);
resolver.setViewNames(new String[] { "*.html" });
return resolver;
}
#Bean
public ViewResolver javascriptViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setContentType("application/javascript");
resolver.setCharacterEncoding(CHARACTER_ENCODING);
resolver.setViewNames(new String[] { "*.js" });
return resolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setMessageSource(messageSource());
engine.addTemplateResolver(htmlTemplateResolver());
engine.addTemplateResolver(javascriptTemplateResolver());
return engine;
}
private ITemplateResolver htmlTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setOrder(0);
resolver.setCheckExistence(true);
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("classpath:templates/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.HTML);
resolver.setSuffix(".html");
return resolver;
}
public ITemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setOrder(1);
resolver.setCheckExistence(true);
resolver.setPrefix("classpath:/static/js/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
return resolver;
}
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource msgSource = new ResourceBundleMessageSource();
msgSource.setAlwaysUseMessageFormat(false);
msgSource.setBasename("messages");
msgSource.setDefaultEncoding(CHARACTER_ENCODING);
msgSource.setFallbackToSystemLocale(true);
msgSource.setUseCodeAsDefaultMessage(false);
return msgSource;
}
}
Basically what happens is we set templateEngine to be a bean, so that we override ThymeleafDefaultConfiguration's default templateEngine implementation bean. This means that each time a template needs to be resolved, the same templateEngine will be used for sure.
We set the order of the htmlTemplateResolver to be 0, and the order of the javascriptTemplateResolver to be 1, so that we will try to resolve each template first in HTML template mode, then in Javascript mode.
It's important, too, that we set the checkExistence flag of the SpringResourceTemplateResolvers to true, because this way if a template can't be found, we will try with the next TemplateResolver.
There is one drawback of this solution, which is we will try to resolve javascript templates unnecessary in HTML mode first, then in JAVASCRIPT mode, so there is an extra step when resolving javascript resources.
I will try to solve the problem better but for now, this works for me.

Problems with controller mapping in Spring MVC

There are similar topics, but they all use xml configuration files. The reason why I'm writing this question is that I'm using annotations.
I experience problems running my app:
getting “WARN org.springframework.web.servlet.PageNotFound - No
mapping found for HTTP request with URI …” when trying to setup
Spring servlet
getting error 404 when trying to run it on server
Here is my code (package and imports are skipped):
1) initializer
public class WebInitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx =
new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.setServletContext(servletContext);
ServletRegistration.Dynamic servlet =
servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
}
}
2) app config
#Configuration
#ComponentScan("ua.kiev.prog")
#EnableWebMvc
public class AppConfig {
#Bean
public EntityManager entityManager() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("AdvJPA");
return emf.createEntityManager();
}
#Bean
public AdvDAO advDAO() {
return new AdvDAOImpl();
}
#Bean
public UrlBasedViewResolver setupViewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
resolver.setOrder(1);
return resolver;
}
#Bean
public CommonsMultipartResolver multipartResolver() {
return new CommonsMultipartResolver();
}
}
3) controller
#Controller
#RequestMapping("/Advertisement")
public class MainController {
#Autowired
private AdvDAO advDAO;
#RequestMapping("/")
public ModelAndView listAdvs() {
return new ModelAndView("index", "advs", advDAO.list());
}
#RequestMapping(value = "/add_page", method = RequestMethod.POST)
public String addPage(Model model) {
return "add_page";
}
#RequestMapping(value = "/search", method = RequestMethod.POST)
public ModelAndView search(#RequestParam(value="pattern") String pattern) {
return new ModelAndView("index", "advs", advDAO.list(pattern));
}
// more code goes here
}
The controller is mapped to /Advertisement, so app should be available at URL localhost:8080/Advertisement/ but it isn't. When I change mapping in annotation to "/" - it becomes available at localhost:8080/Advertisement/. How can it be?
And when I change it back to "/Advertisement" - the same probleb accurs (error 404 and exception "No mapping found for HTTP request with URI …")
So, where I've made a mistake in my code?
Or maybe the problem is in Eclipse/TomCat/Maven?
Source - https://github.com/KostyantynPanchenko/prog.kiev.ua.lesson09.adv
You should change mapping
#Controller
#RequestMapping("/")
public class MainController {
#Autowired
private AdvDAO advDAO;
#RequestMapping("/Advertisement")
public ModelAndView listAdvs() {
return new ModelAndView("index", "advs", advDAO.list());
}
The mistake that a mapper used the value from the annotation to match the request URL, and it can't match the last slash. Note, it should not happen in the above code.
How are you running the application? Atleast in tomcat each deployed application is served from specific context path. Context path is determined from the base file name, more on that here.
So if you're deploying Advertisement.war all requests to the app will be served from localhost:8080/Advertisement/ even though you're declaring the DispatcherServlet and Controller to /

Spring Boot + Thymeleaf + Apache tiles - cannot find a definition file

I have a weird problem with my configuration. I'm trying to configure apache tiles with thymeleaf. I'm trying to do the same things they are on theirs documentation page.
I have following configuration file:
#Configuration
public class ViewConfig {
#Bean
public ViewResolver tilesViewResolver() {
ThymeleafViewResolver vr = new ThymeleafViewResolver();
vr.setTemplateEngine(templateEngine());
vr.setViewClass(ThymeleafTilesView.class);
vr.setCharacterEncoding("UTF-8");
vr.setOrder(Ordered.LOWEST_PRECEDENCE);
return vr;
}
#Bean
public ViewResolver thymeleafViewResolver() {
ThymeleafViewResolver vr = new ThymeleafViewResolver();
vr.setTemplateEngine(templateEngine());
vr.setCharacterEncoding("UTF-8");
vr.setOrder(Ordered.HIGHEST_PRECEDENCE);
// all message/* views will not be handled by this resolver;
vr.setExcludedViewNames(new String[]{"message/*"});
return vr;
}
#Bean
public SpringTemplateEngine templateEngine() {
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.addDialect(new TilesDialect());
templateEngine.setTemplateResolver(templateResolver());
return templateEngine;
}
#Bean
public ThymeleafTilesConfigurer tilesConfigurer() {
ThymeleafTilesConfigurer ttc = new ThymeleafTilesConfigurer();
ttc.setValidateDefinitions(false);
ttc.setDefinitions(new String[]{"/templates/tiles-defs.xml"});
return ttc;
}
#Bean
public TemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setSuffix(".html");
resolver.setPrefix("templates/");
resolver.setTemplateMode("HTML5");
resolver.setCharacterEncoding("UTF-8");
return resolver;
}
}
I've added TemplateResolver for my purposes, but removing it doesn't help (for having the same configuration as documentation).
This is my resource folder structure:
And the problem is that I've got following exception:
Caused by: java.io.FileNotFoundException: ServletContext resource [/templates/tiles-defs.xml] cannot be resolved to URL because it does not exist
The most interesting is that I've checked target/classes (which is in classpath) and there is the file templates/tiles-defs.xml.
I've tried a couple of times to change the definition path (also I've tried to remove definition and get it from default WEB-INF/tiles.xml path) but no outcome - same exception.
What I'm doing wrong?
Here is how I solved it:
#Bean
public ThymeleafTilesConfigurer tilesConfigurer() {
ThymeleafTilesConfigurer ttc = new ThymeleafTilesConfigurer();
//ttc.setValidateDefinitions(false);
ttc.setDefinitions(ThymeleafProperties.DEFAULT_PREFIX + "tiles-defs.xml");
return ttc;
}

Spring MVC. No mapping found for HTTP request with URI

What is wrong with my views?
#EnableWebMvc
#Configuration
class WebMvcConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/view/");
resolver.setSuffix(".jsp");
return resolver;
}
}
#Configuration
#ComponentScan(basePackages = "com.wordparty")
class AppConfig {
}
#Controller
class IndexController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public String showIndex() {
return "index";
}
}
Here is my package structure
main
--java
----com.wordparty
...
--webapp
----WEB-INF
------view
--------index.jsp
I recieve 404 response. And in console I see message that my page wasn't found. If I write #ResponseBody then it returns text from controller without problems...
Where is a mistake?
P.S. I am using Spring 4 + Java 8
I solve the problem.. my dispatcher servlet has /* mapping instead of /

Spring Web MVC: How to change viewResolver prefix according to client request

I have two folders inside my WEB-INF directory 1st is views which has desktop website related template files and 2nd one mobile_views has mobile website template. I am using WebConfiguration class file and define required #Bean functions. Now I want to change the viewResolver.setPrefix(viewFolderName) according to the request. If user hits the website from mobile so will set mobile_views otherwise It will views. So I am detecting the device browser and setting the viewFolderName but seems to work only one time because WebConfiguration class getting accessed when server starts, that's why I am facing this problem. Here is my code, please give me some solution for this.
#Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass( JstlView.class );
if(isRequestFromMobile()) viewResolver.setPrefix("/WEB-INF/mobile_views/");
else viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
private #Autowired HttpServletRequest request;
private boolean isRequestFromMobile() {
String userAgent = request.getHeader("User-Agent");
String httpAccept = request.getHeader("Accept");
UAgentInfo detector = new UAgentInfo(userAgent, httpAccept);
System.out.println("### User Agent: "+userAgent);
if (detector.detectMobileQuick()) {
return true;
}
return false;
}
Take a look at this tutorial from Spring's site that depends on Spring Boot.
The meat is that you need to configure DeviceResolverHandlerInterceptor and DeviceHandlerMethodArgumentResolver.
Once that is done, you can use the controller method's Device argument to differentiate the handling depending on the device
A step further from that point would be to integrate LiteDeviceDelegatingViewResolver (provided by Spring Mobile) in the following manner:
#Bean
public LiteDeviceDelegatingViewResolver liteDeviceAwareViewResolver() {
InternalResourceViewResolver delegate =
new InternalResourceViewResolver();
delegate.setPrefix("/WEB-INF/views/");
delegate.setSuffix(".jsp");
LiteDeviceDelegatingViewResolver resolver =
new LiteDeviceDelegatingViewResolver(delegate);
resolver.setMobilePrefix("mobile/");
resolver.setTabletPrefix("tablet/");
return resolver;
}

Categories