return thymeleaf template String add url parameter not work in spring boot with thymeleaf - spring

I have a problem with the practice code .
It's fail with formLogin?error
How can i add error url parameter with formLogin page.
When only return formLogin, it's success.
my backend code:
#GetMapping(value = "/formlogin")
public String formLogin(HttpServletRequest request, HttpServletResponse response) {
..........
return "formLogin";
}//
#GetMapping(value = "/formLoginPost")
public String formLoginPost(HttpServletRequest request, HttpServletResponse response) {
//return "formLogin?error";
return "redirect:formLogin?error";
}
and with the formLogin page code
<!DOCTYPE html>
<html xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
xmlns:th="http://www.thymeleaf.org"
layout:decorate="~{fragments/layout}">
<head>
<title>Login</title>
</head>
<body>
<div layout:fragment="content" th:remove="tag">
<div class="row">
<div class="col-md-6 col-md-offset-3">
<h1>Login page</h1>
<form th:action="#{/login}" method="post">
<div th:if="${param.error}">
<div class="alert alert-danger">
Invalid username or password.
</div>
</div>
.......
</body>
</html>

seanwang. The correct way is using Spring-Security where you can declare successful and fail URLs for authentication form. And then you can make what you need in your controller with the specified URL. The Spring-Security add security filter and validators in your application. And when the user try authenticate, filter will check if the Principal is present in Session. If yes, the user will be redirected to the successful url, if not - to the fail URL.

Related

I can't show an html page

I have to develop a project for college using Spring. I started watching some tutorials and I can't show an html page. I do the same but it returns only one string.
I'm using visual studio code.
Controller:
package com.example.springteste.controllers;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class ProductController {
#GetMapping("/formulario")
public String formulario()
{
return "form";
}
}
My view is just inside templates
View:
<!DOCTYPE html>
<html lang="pt-br">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<main>
<section id="sectionProduct">
<div>
<div id="sectionProduct-title">
<h1>
Título
</h1>
</div>
<div id="sectionProduct-form">
<form action="">
<div class="sectionProduct-form-inputLabel">
<input type="text" id="title" name="title">
<label for="title">
Título
</label>
</div>
</form>
</div>
</div>
</section>
</main>
</body>
</html>
I believe something is missing. I am a beginner in java and spring
If you want to return a view name from the controller method handler - you have to use #Controller annotation. Also make sure you put your views into the correct directory, according to your view resolver configuration.
About #RestController - as the name suggests, it shall be used in case of REST style controllers i.e. handler methods shall return the JSON/XML response directly to the client rather using view resolvers.
In your case the handler method will return "form" to the client.

Thymeleaf switch statement including every case

I'm working on my first Spring MVC project and I got stuck at a problem. I have 3 types of users in my application: admin, employee and customer. Depending on the type of user, I would like to have a specific type of menu for each of them. I tried using a switch statement in my thymeleaf template but every case gets included in the output and I don't understand why.
This is the code for my method in the controller:
#RequestMapping(value = "list/{roleId}", method = RequestMethod.GET)
public String listFood(Model model, #PathVariable int roleId){
model.addAttribute("title", "Available Foods");
model.addAttribute("roleId", roleId);
model.addAttribute("foods", foodDao.findAll());
return "food/list";
}
And this is the code from the Thymeleaf template (each fragment will be included in the resulting page):
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments :: head"></head>
<body class="container">
<h1 th:text="${title}">Food</h1>
<div th:switch="${roleId}">
<p th:case="0"><nav th:replace="admin-fragments :: navigation"></nav></p>
<p th:case="1"><nav th:replace="employee-fragments :: navigation"></nav></p>
<p th:case="2"><nav th:replace="customer-fragments :: navigation"></nav></p>
</div>
</body>
</html>
However, if I change the template to the following one then only the correct case will be included in the resulting page.
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org/">
<head th:replace="fragments :: head"></head>
<body class="container">
<h1 th:text="${title}">Food</h1>
<div th:switch="${roleId}">
<p th:case="0">User is an administrator</p>
<p th:case="1">User is an employee</p>
<p th:case="2">User is a customer</p>
</div>
</body>
</html>
Why isn't the switch from the first template behaving like the one in the second template? What should I change in the first template to be able to have a personalised menu for each type of user? Thank you!

Thymeleaf unable to load static resources

I have a spring web application that loads the templates correctly, but does not load static assets like CSS and images. I have added my static resources within the src/main/resources/static directory, added thymeleaf annotations, added configurations and checked them against similar questions here on StackOverflow, but none of my CSS/image files are getting loaded:
Console Error
Refused to apply style from 'http://localhost:8080/login' because its MIME type ('text/html') is not a supported stylesheet MIME type, and strict MIME checking is enabled.
Directory Structure
TemplateConfig.java
package com.valencra.recipes.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.extras.springsecurity4.dialect.SpringSecurityDialect;
import org.thymeleaf.spring4.SpringTemplateEngine;
import org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
#Configuration
public class TemplateConfig {
#Bean
public SpringResourceTemplateResolver templateResolver() {
final SpringResourceTemplateResolver templateResolver = new SpringResourceTemplateResolver();
templateResolver.setPrefix("classpath:/templates/");
templateResolver.setSuffix(".html");
templateResolver.setTemplateMode("LEGACYHTML5");
return templateResolver;
}
#Bean
public SpringTemplateEngine templateEngine() {
final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine();
springTemplateEngine.addTemplateResolver(templateResolver());
springTemplateEngine.addDialect(new SpringSecurityDialect());
return springTemplateEngine;
}
#Bean
public ThymeleafViewResolver viewResolver() {
final ThymeleafViewResolver viewResolver = new ThymeleafViewResolver();
viewResolver.setTemplateEngine(templateEngine());
viewResolver.setOrder(1);
return viewResolver;
}
}
layout.html
<!DOCTYPE html>
<html lang="en">
<head th:fragment="head">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>My Recipes</title>
<link href='https://fonts.googleapis.com/css?family=Varela+Round' rel='stylesheet' type='text/css'>
<link rel="stylesheet" th:href="#{/css/unsemantic-grid-responsive.css}">
<link rel="stylesheet" th:href="#{/css/styles.css}">
</head>
<body>
<nav th:fragment="nav">
<a th:href="#{|/profile|}" th:text="${currentUser.name}">
Chandra S.
</a>
•
<form th:action="#{/logout}" method="post" style="display: inline">
logout
</form>
</nav>
<div th:fragment="home">
<div class="grid-100">
<a th:href="#{|/|}">
<h1>
<img th:src="#{/images/chefbot.svg}" height="60px">
<br>
My Recipes
</h1>
</a>
</div>
</div>
<div th:fragment="favorite">
<form th:action="#{|/recipes/${recipe.id}/favorite|}" method="post" style="display:inline">
<button type="submit">
<img th:src="${recipe.isFavorite(currentUser)} ? #{/images/favorited.svg} : #{/images/favorite.svg}" style="height: 15px;">
</button>
</form>
</div>
</body>
</html>
login.html
<!doctype html>
<html lang="en">
<head th:replace="layout :: head"></head>
<body>
<nav>
<a th:href="#{/signup}">
sign-up
</a>
</nav>
<div class="grid-container">
<div th:replace="layout :: home"></div>
<div class="grid-100">
<div class="recipes">
<form th:action="#{|/login|}" method="post" th:object="${user}">
<div class="prefix-20 grid-60 suffix-20">
<p>
<input placeholder="Username" th:field="*{username}"> </input>
</p>
</div> <div class="clear"></div>
<div class="prefix-20 grid-60 suffix-20">
<p>
<input placeholder="Password" type="password" th:field="*{password}"> </input>
</p>
</div> <div class="clear"></div>
<div class="prefix-20 grid-60 suffix-20">
<p>
<button>Login</button>
</p>
</div> <div class="clear"></div>
</form>
</div> <!-- recipes -->
</div> <!-- grid-100 -->
</div> <!-- grid-container -->
</body>
</html>
It looks like your CSS is served as HTML.
Since you put the Stylesheets in src/main/resources/static Spring should set up most things based on sensible defaults, including setting the correct content-type header.
I assume you are using Spring Security, since you showed a login form.
It could be that your resources are protected by Spring Security. Thus instead of your stylesheet you get a 30x redirect to the login page. This would explain why your browser complains about the text/html content type, which is wrong for css but the correct type for a login page.
Do you configure HttpSecurity somewhere in your project (most likely annotated with #EnableWebSecurity)?
If so, you need to allow anonymous access to your resources:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/css/**", "/images/**").permitAll()
// rest of your code
If you switch to the network tab in your browser console, you should be able to inspect the redirect to the login page.
What helped in my case was answer from #phisch and the following code
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(
"/webjars/**",
"/img/**",
"/css/**",
"/js/**")
.addResourceLocations(
"classpath:/META-INF/resources/webjars/",
"classpath:/static/img/",
"classpath:/static/css/",
"classpath:/static/js/");
}
}

Thymleaf - how to call model Attribute on html document

Im trying to render a webapge and use the thymleaf attribute "url" added with model.addAttribute but the Attribute is not beeing displayed on the html document.
My document.html file path is here:
/templates/webpage/document.html
#RequestMapping(value = "/webpage/document")
public String document(HttpServletRequest req, Model model) {
model.addAttribute("dialogurl", url);
return "/webpage/document";
}
Here is the html document
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:include="wrapperdialog :: page">
<head>
<title></title>
</head>
<body>
<div th:fragment="content">
<div class="container dialogpage">
<div class="row">
<div class="col-md-12">
<div id="typeform" th:attr="data-url=*{dialogurl}">
</div>
</div>
</div>
</div>
</div>
Please use this expression to bind dialog url: #{${dialogurl}}
<div id="typeform" th:attr="data-url=#{${dialogurl}}">
# prefix is used to specify a link and $ prefix is used to bind your model value.
Use $ to bind data.
<div id="typeform" th:attr="data-url=${dialogurl}">

Thymeleaf + spring dynamic replace

Is it possible to create a dynamic replace in Thymeleaf?
I have the following controller:
#Controller
public class LoginController {
#RequestMapping("/login")
public String getLogin(Model model){
model.addAttribute("template","login");
return "index";
}
}
And the following view:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" >
<head></head>
<body>
<div th:replace="fragments/${template} :: ${template}"></div>
</body>
</html>
And i'm getting the following error:
Error resolving template "fragments/${template}", template might not exist or might not be accessible by any of the configured Template Resolvers
UPDATE
I tried to preprocess my variables like this:
<div th:replace="fragments/${__#{${template}}__} :: ${__#{${template}}__}"></div>
How ever now ${template} is getting replaced with login i have the following error now:
Exception evaluating SpringEL expression: "??login_en_US??"
Although Joe Essey's solution is working as well i solved with following code:
<div th:replace="#{'fragments/' + ${template}} :: ${template}"></div>
I believe the appropriate method to manage this behavior in thymeleaf is to use layout:fragment tags. Please correct me if I'm wrong. Here is a simple example of my layout page, and the login page which is 'dynamically' loaded:
layout.html
<html xmlns:layout="http://www.w3.org/1999/xhtml" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
<title layout:title-pattern="$DECORATOR_TITLE - $CONTENT_TITLE">Layout</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge"/>
</head>
<body>
<div>
<div class="app-container">
<div th:fragment="content">
</div>
</div>
</div>
<div th:fragment="script"></div>
</body>
</html>
Then, when login gets loaded, it replaces the th:fragment div with the associated div in the html view which matches the string returned by the controller method, in this case login.html:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.w3.org/1999/xhtml"
layout:decorator="layout">
<head>
<title>Login</title>
</head>
<body>
<div th:fragment="content">
<form th:action="#{/login}" method="post">
<div><label> User Name : <input type="text" name="username"/> </label></div>
<div><label> Password: <input type="password" name="password"/> </label></div>
<div><input type="submit" value="Sign In"/></div>
</form>
</div>
</body>
</html>
Now, if you want to load another fragment conditionally, the approach I take is to add replace tags with th:if cases. Here's an example of a Form that displays different questions based on an attribute of the current user:
<div th:if="${foo.type)} == 'type_1'">
<div th:replace="fragments/custom-questions :: type-1-checkboxes"></div>
</div>
<div th:if="${foo.type} == 'type_2'">
<div th:replace="fragments/custom-questions :: type-2-checkboxes"></div>
</div>
Then the associated div gets loaded from the file custom-questions.html:
<div th:fragment="type-1-checkboxes">
//stuff
</div>
<div th:fragment="type-2-checkboxes">
//stuff
</div>
I am just encountering this issue (this is my first time with thymeleaf/spring). This is what solved it for me:
<div class="col-md-12" th:include="__${template}__ :: body" ...
In Thymeleaf 3.0, the following solution has worked for me:
<div th:replace="('fragments/' + ${template}) :: (${template})">
(Note however, that I use it with fixed name of the fragment and dynamic name of the template, so the parantheses around :: (${template}) might be optional.)
The solution is inspired by documentation for Thymeleaf in https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#fragment-specification-syntax
Both templatename and selector in the above examples can be fully-featured expressions (even conditionals!) like:
<div th:insert="footer :: (${user.isAdmin}? #{footer.admin} : #{footer.normaluser})"></div>
Note again how the surrounding ~{...} envelope is optional in th:insert/th:replace
<div th:insert=“${subpage}::fragementName”>
Just change subpage names and you will dynamic behaviour in thymleaf

Resources