Thymeleaf inlining and natural processing (#711)

* Expression-Based Access Control

PermitAll, hasRole, hasAnyRole etc.
I modified classes regards to Security

* Added test cases for Spring Security Expressions

* Handler Interceptor - logging example

* Test for logger interceptor

* Removed conflicted part

* UserInterceptor (adding user information to model)

* Spring Handler Interceptor - session timers

* Spring Security CSRF attack protection with Thymeleaf

* Fix and();

* Logger update

* Changed config for Thymeleaf

* Thymeleaf Natural Processing and Inlining
This commit is contained in:
maibin 2016-10-01 10:59:54 +02:00 committed by Grzegorz Piwowarek
parent c5fd46e5f2
commit 72b2eacb4f
11 changed files with 205 additions and 64 deletions

View File

@ -20,6 +20,8 @@ import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;
import com.baeldung.thymeleaf.formatter.NameFormatter;
import com.baeldung.thymeleaf.utils.ArrayUtil;
@Configuration
@EnableWebMvc
@ -37,30 +39,67 @@ public class WebMVCConfig extends WebMvcConfigurerAdapter implements Application
}
@Bean
public ViewResolver viewResolver() {
public ViewResolver htmlViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setTemplateEngine(templateEngine(htmlTemplateResolver()));
resolver.setContentType("text/html");
resolver.setCharacterEncoding("UTF-8");
resolver.setOrder(1);
resolver.setViewNames(ArrayUtil.array("*.html"));
return resolver;
}
@Bean
public TemplateEngine templateEngine() {
SpringTemplateEngine engine = new SpringTemplateEngine();
engine.setEnableSpringELCompiler(true);
engine.setTemplateResolver(templateResolver());
return engine;
}
public ViewResolver javascriptViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine(javascriptTemplateResolver()));
resolver.setContentType("application/javascript");
resolver.setCharacterEncoding("UTF-8");
resolver.setViewNames(ArrayUtil.array("*.js"));
return resolver;
}
@Bean
public ViewResolver plainViewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine(plainTemplateResolver()));
resolver.setContentType("text/plain");
resolver.setCharacterEncoding("UTF-8");
resolver.setViewNames(ArrayUtil.array("*.txt"));
return resolver;
}
private ITemplateResolver templateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".html");
resolver.setTemplateMode(TemplateMode.HTML);
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("/WEB-INF/views/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.HTML);
return resolver;
}
private ITemplateResolver javascriptTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/js/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.JAVASCRIPT);
return resolver;
}
private ITemplateResolver plainTemplateResolver() {
SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
resolver.setApplicationContext(applicationContext);
resolver.setPrefix("/WEB-INF/txt/");
resolver.setCacheable(false);
resolver.setTemplateMode(TemplateMode.TEXT);
return resolver;
}
@Bean
@Description("Spring Message Resolver")

View File

@ -21,7 +21,7 @@ public class HomeController {
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, Locale.getDefault());
model.addAttribute("serverTime", dateFormat.format(new Date()));
return "home";
return "home.html";
}
}

View File

@ -0,0 +1,33 @@
package com.baeldung.thymeleaf.controller;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.baeldung.thymeleaf.utils.StudentUtils;
@Controller
public class InliningController {
@RequestMapping(value = "/html", method = RequestMethod.GET)
public String getExampleHTML(Model model) {
model.addAttribute("title", "Baeldung");
model.addAttribute("description", "<strong>Thymeleaf</strong> tutorial");
return "inliningExample.html";
}
@RequestMapping(value = "/js", method = RequestMethod.GET)
public String getExampleJS(Model model) {
model.addAttribute("students", StudentUtils.buildStudents());
return "studentCheck.js";
}
@RequestMapping(value = "/plain", method = RequestMethod.GET)
public String getExamplePlain(Model model) {
model.addAttribute("username", SecurityContextHolder.getContext().getAuthentication().getName());
model.addAttribute("students", StudentUtils.buildStudents());
return "studentsList.txt";
}
}

View File

@ -1,11 +1,9 @@
package com.baeldung.thymeleaf.controller;
import java.util.ArrayList;
import java.util.List;
import javax.validation.Valid;
import com.baeldung.thymeleaf.model.Student;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
@ -13,6 +11,9 @@ import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.baeldung.thymeleaf.model.Student;
import com.baeldung.thymeleaf.utils.StudentUtils;
/**
* Handles requests for the student model.
*
@ -20,50 +21,30 @@ import org.springframework.web.bind.annotation.RequestMethod;
@Controller
public class StudentController {
@RequestMapping(value = "/saveStudent", method = RequestMethod.POST)
public String saveStudent(@Valid @ModelAttribute Student student, BindingResult errors, Model model) {
if (!errors.hasErrors()) {
// get mock objects
List<Student> students = buildStudents();
// add current student
students.add(student);
model.addAttribute("students", students);
}
return ((errors.hasErrors()) ? "addStudent" : "listStudents");
}
@RequestMapping(value = "/saveStudent", method = RequestMethod.POST)
public String saveStudent(@Valid @ModelAttribute Student student, BindingResult errors, Model model) {
if (!errors.hasErrors()) {
// get mock objects
List<Student> students = StudentUtils.buildStudents();
// add current student
students.add(student);
model.addAttribute("students", students);
}
return ((errors.hasErrors()) ? "addStudent.html" : "listStudents.html");
}
@RequestMapping(value = "/addStudent", method = RequestMethod.GET)
public String addStudent(Model model) {
model.addAttribute("student", new Student());
return "addStudent";
}
@RequestMapping(value = "/addStudent", method = RequestMethod.GET)
public String addStudent(Model model) {
model.addAttribute("student", new Student());
return "addStudent.html";
}
@RequestMapping(value = "/listStudents", method = RequestMethod.GET)
public String listStudent(Model model) {
@RequestMapping(value = "/listStudents", method = RequestMethod.GET)
public String listStudent(Model model) {
model.addAttribute("students", buildStudents());
model.addAttribute("students", StudentUtils.buildStudents());
return "listStudents";
}
return "listStudents.html";
}
private List<Student> buildStudents() {
List<Student> students = new ArrayList<Student>();
Student student1 = new Student();
student1.setId(1001);
student1.setName("John Smith");
student1.setGender('M');
student1.setPercentage(Float.valueOf("80.45"));
students.add(student1);
Student student2 = new Student();
student2.setId(1002);
student2.setName("Jane Williams");
student2.setGender('F');
student2.setPercentage(Float.valueOf("60.25"));
students.add(student2);
return students;
}
}

View File

@ -0,0 +1,8 @@
package com.baeldung.thymeleaf.utils;
public class ArrayUtil {
public static String[] array(String... args) {
return args;
}
}

View File

@ -0,0 +1,34 @@
package com.baeldung.thymeleaf.utils;
import java.util.ArrayList;
import java.util.List;
import com.baeldung.thymeleaf.model.Student;
public class StudentUtils {
private static List<Student> students = new ArrayList<Student>();
public static List<Student> buildStudents() {
if (students.isEmpty()){
Student student1 = new Student();
student1.setId(1001);
student1.setName("John Smith");
student1.setGender('M');
student1.setPercentage(Float.valueOf("80.45"));
students.add(student1);
Student student2 = new Student();
student2.setId(1002);
student2.setName("Jane Williams");
student2.setGender('F');
student2.setPercentage(Float.valueOf("60.25"));
students.add(student2);
}
return students;
}
}

View File

@ -0,0 +1,2 @@
var count = [[${students.size()}]];
alert("Number of students in group: " + count);

View File

@ -0,0 +1,8 @@
Dear [(${username})],
This is the list of our students:
[# th:each="s : ${students}"]
- [(${s.name})]. ID: [(${s.id})]
[/]
Thanks,
The Baeldung University

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Inlining example</title>
</head>
<body>
<p>Title of tutorial: [[${title}]]</p>
<p>Description: [(${description})]</p>
</body>
</html>

View File

@ -4,6 +4,15 @@
<head>
<title>Student List</title>
</head>
<script type="text/javascript"
src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.js"></script>
<script th:inline="javascript">
$(document).ready(function() {
$.ajax({
url : "/spring-thymeleaf/js",
});
});
</script>
<body>
<h1>Student List</h1>
<table border="1">

View File

@ -2,8 +2,8 @@ package org.baeldung.security.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.user;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import javax.servlet.Filter;
@ -59,5 +59,20 @@ public class CsrfEnabledIntegrationTest {
public void addStudentWithCSRF() throws Exception {
mockMvc.perform(post("/saveStudent").contentType(MediaType.APPLICATION_JSON).param("id", "1234567").param("name", "Joe").param("gender", "M").with(testUser()).with(csrf())).andExpect(status().isOk());
}
@Test
public void htmlInliningTest() throws Exception {
mockMvc.perform(get("/html").with(testUser()).with(csrf())).andExpect(status().isOk()).andExpect(view().name("inliningExample.html"));
}
@Test
public void jsInliningTest() throws Exception {
mockMvc.perform(get("/js").with(testUser()).with(csrf())).andExpect(status().isOk()).andExpect(view().name("studentCheck.js"));
}
@Test
public void plainInliningTest() throws Exception {
mockMvc.perform(get("/plain").with(testUser()).with(csrf())).andExpect(status().isOk()).andExpect(view().name("studentsList.txt"));
}
}