Tạo ứng dụng đăng ký tài khoản với Spring Boot, Spring Form Validation
1. Mục tiêu của ví dụ
Tài liệu được viết dựa trên:
Eclipse 3.7 (Oxygen)
Spring Boot 2.x
Spring Validation
Thymeleaf
Trong bài viết này tôi hướng dẫn bạn tạo một ứng dụng đăng ký người dùng sử dụng Spring Boot + Spring Validation + Thymeleaf. Các chủ đề được đề cập trong bài viết này bao gồm:
- Tạo một Form đăng ký trên Spring.
- Sử dụng Spring Validator để xác thực (validate) thông tin người dùng đã nhập vào.
- Giải thích nguyên tắc hoạt động của Spring Validator.
Xem trước ứng dụng:
2. Tạo Spring Boot project
Trên Eclipse tạo dự án Spring Boot.
Nhập vào:
- Name: SbRegistrationFormValidationThymeleaf
- Group: org.o7planning
- Description: Spring Boot + Form Validation + Thymeleaf
- Package: org.o7planning.sbformvalidation
Lựa chọn các công nghệ và các thư viện để sử dụng:
- Security
- Validation
- Web
- Thymeleaf
OK, Project đã được tạo ra.
SbRegistrationFormValidationThymeleafApplication.java
package org.o7planning.sbformvalidation;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SbRegistrationFormValidationThymeleafApplication {
public static void main(String[] args) {
SpringApplication.run(SbRegistrationFormValidationThymeleafApplication.class, args);
}
}
3. Cấu hình pom.xml
Trong ví dụ này chúng ta sẽ sử dụng thư viện Commons Validation để kiểm tra email mà người dùng nhập vào có chính xác hay không. Vì vậy cần phải khai báo thư viện này trong pom.xml.
** Commons Validation **
<dependencies>
.....
<!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.6</version>
</dependency>
.....
</dependencies>
Toàn bộ nội dung tập tin pom.xml:
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.o7planning</groupId>
<artifactId>SbRegistrationFormValidationThymeleaf</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>SbRegistrationFormValidationThymeleaf</name>
<description>Spring Boot + Form Validation + Thymeleaf</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-validator/commons-validator -->
<dependency>
<groupId>commons-validator</groupId>
<artifactId>commons-validator</artifactId>
<version>1.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
4. Security, MessageSource
Trong ví dụ này không tập trung vào vấn đề bảo mật của ứng dụng. Tuy nhiên chúng ta cần tới thư viện Spring Security để có thể mật mã hóa (encode) mật khẩu của người dùng trước khi lưu vào cơ sở dữ liệu. Và bạn cần phải khai báo một Spring BEAN cho việc mật mã hóa mật khẩu.
WebSecurityConfig.java
package org.o7planning.sbformvalidation.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
return bCryptPasswordEncoder;
}
// Trong ví dụ này chúng ta không sử dụng Security.
// Ghi đè phương thức này với Code rỗng
// để vô hiệu hóa Security mặc định của Spring Boot.
@Override
protected void configure(HttpSecurity http) throws Exception {
// Empty code!
}
}
Trong ví dụ này chúng ta có tập tin validation.properties. Tập tin này chứa các mã lỗi (Error code), được sử dụng để thông báo cho người dùng khi họ nhập các thông tin không chính xác.
validation.properties
NotEmpty.appUserForm.userName=User name is required
NotEmpty.appUserForm.firstName=First Name is required
NotEmpty.appUserForm.lastName=Last name is required
NotEmpty.appUserForm.email=Email is required
NotEmpty.appUserForm.password=Password is required
NotEmpty.appUserForm.confirmPassword=Confirm Password is required
NotEmpty.appUserForm.gender=Gender is required
NotEmpty.appUserForm.countryCode=Country is required
Pattern.appUserForm.email=Invalid email
Duplicate.appUserForm.email=Email has been used by another account
Duplicate.appUserForm.userName=Username is not available
Match.appUserForm.confirmPassword=Password does not match the confirm password
Bạn cần khai báo một MessageResource Spring Bean, để Spring tự động tải (load) nội dung của tập tin validation.properties vào bộ nhớ.
WebConfiguration.java
package org.o7planning.sbformvalidation.config;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
public class WebConfiguration implements WebMvcConfigurer {
@Bean
public MessageSource messageSource() {
ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
// Tải file: validation.properties
messageSource.setBasename("classpath:validation");
messageSource.setDefaultEncoding("UTF-8");
return messageSource;
}
}
5. Model, DAO
Lớp AppUser đại diện cho một bản ghi (record) của bảng APP_USER. Nó là một người dùng (user) đã đăng ký thành công vào hệ thống.
AppUser.java
package org.o7planning.sbformvalidation.model;
public class AppUser {
private Long userId;
private String userName;
private String firstName;
private String lastName;
private boolean enabled;
private String gender;
private String email;
private String encrytedPassword;
private String countryCode;
public AppUser() {
}
public AppUser(Long userId, String userName, String firstName, String lastName, //
boolean enabled, String gender, //
String email,String countryCode, String encrytedPassword) {
super();
this.userId = userId;
this.userName = userName;
this.firstName = firstName;
this.lastName = lastName;
this.enabled = enabled;
this.gender = gender;
this.email = email;
this.countryCode= countryCode;
this.encrytedPassword = encrytedPassword;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getEncrytedPassword() {
return encrytedPassword;
}
public void setEncrytedPassword(String encrytedPassword) {
this.encrytedPassword = encrytedPassword;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
}
Country.java
package org.o7planning.sbformvalidation.model;
public class Country {
private String countryCode;
private String countryName;
public Country() {
}
public Country(String countryCode, String countryName) {
this.countryCode = countryCode;
this.countryName = countryName;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getCountryName() {
return countryName;
}
public void setCountryName(String countryName) {
this.countryName = countryName;
}
}
Gender.java
package org.o7planning.sbformvalidation.model;
public class Gender {
public static final String MALE = "M";
public static final String FEMALE = "F";
}
Các lớp DAO (Data Access Object) được sử dụng để thao tác với nguồn dữ liệu, chẳng hạn như query, insert, update, delete. Các lớp này thường được chú thích (annotate) bởi @Repository để Spring quản lý chúng như các Spring BEAN.
AppUserDAO.java
package org.o7planning.sbformvalidation.dao;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.o7planning.sbformvalidation.formbean.AppUserForm;
import org.o7planning.sbformvalidation.model.AppUser;
import org.o7planning.sbformvalidation.model.Gender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Repository;
@Repository
public class AppUserDAO {
// Config in WebSecurityConfig
@Autowired
private PasswordEncoder passwordEncoder;
private static final Map<Long, AppUser> USERS_MAP = new HashMap<>();
static {
initDATA();
}
private static void initDATA() {
String encrytedPassword = "";
AppUser tom = new AppUser(1L, "tom", "Tom", "Tom", //
true, Gender.MALE, "tom@waltdisney.com", encrytedPassword, "US");
AppUser jerry = new AppUser(2L, "jerry", "Jerry", "Jerry", //
true, Gender.MALE, "jerry@waltdisney.com", encrytedPassword, "US");
USERS_MAP.put(tom.getUserId(), tom);
USERS_MAP.put(jerry.getUserId(), jerry);
}
public Long getMaxUserId() {
long max = 0;
for (Long id : USERS_MAP.keySet()) {
if (id > max) {
max = id;
}
}
return max;
}
//
public AppUser findAppUserByUserName(String userName) {
Collection<AppUser> appUsers = USERS_MAP.values();
for (AppUser u : appUsers) {
if (u.getUserName().equals(userName)) {
return u;
}
}
return null;
}
public AppUser findAppUserByEmail(String email) {
Collection<AppUser> appUsers = USERS_MAP.values();
for (AppUser u : appUsers) {
if (u.getEmail().equals(email)) {
return u;
}
}
return null;
}
public List<AppUser> getAppUsers() {
List<AppUser> list = new ArrayList<>();
list.addAll(USERS_MAP.values());
return list;
}
public AppUser createAppUser(AppUserForm form) {
Long userId = this.getMaxUserId() + 1;
String encrytedPassword = this.passwordEncoder.encode(form.getPassword());
AppUser user = new AppUser(userId, form.getUserName(), //
form.getFirstName(), form.getLastName(), false, //
form.getGender(), form.getEmail(), form.getCountryCode(), //
encrytedPassword);
USERS_MAP.put(userId, user);
return user;
}
}
CountryDAO.java
package org.o7planning.sbformvalidation.dao;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.o7planning.sbformvalidation.model.Country;
import org.springframework.stereotype.Repository;
@Repository
public class CountryDAO {
private static final Map<String, Country> COUNTRIES_MAP = new HashMap<>();
static {
initDATA();
}
private static void initDATA() {
Country vn = new Country("VN", "Vietnam");
Country en = new Country("EN", "England");
Country fr = new Country("FR", "France");
Country us = new Country("US", "US");
Country ru = new Country("RU", "Russia");
COUNTRIES_MAP.put(vn.getCountryCode(), vn);
COUNTRIES_MAP.put(en.getCountryCode(), en);
COUNTRIES_MAP.put(fr.getCountryCode(), fr);
COUNTRIES_MAP.put(us.getCountryCode(), us);
COUNTRIES_MAP.put(ru.getCountryCode(), ru);
}
public Country findCountryByCode(String countryCode) {
return COUNTRIES_MAP.get(countryCode);
}
public List<Country> getCountries() {
List<Country> list = new ArrayList<>();
list.addAll(COUNTRIES_MAP.values());
return list;
}
}
6. Form Bean, Validator
Lớp AppUserForm đại diện cho dữ liệu mà người dùng cần phải nhập trên Form đăng ký.
AppUserForm.java
package org.o7planning.sbformvalidation.formbean;
public class AppUserForm {
private Long userId;
private String userName;
private String firstName;
private String lastName;
private boolean enabled;
private String gender;
private String email;
private String password;
private String confirmPassword;
private String countryCode;
public AppUserForm() {
}
public AppUserForm(Long userId, String userName, //
String firstName, String lastName, boolean enabled, //
String gender, String email, String countryCode, //
String password, String confirmPassword) {
this.userId = userId;
this.userName = userName;
this.firstName = firstName;
this.lastName = lastName;
this.enabled = enabled;
this.gender = gender;
this.email = email;
this.countryCode = countryCode;
this.password = password;
this.confirmPassword = confirmPassword;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getCountryCode() {
return countryCode;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getConfirmPassword() {
return confirmPassword;
}
public void setConfirmPassword(String confirmPassword) {
this.confirmPassword = confirmPassword;
}
}
Lớp AppUserValidator được sử dụng để phê chuẩn (validate) các thông tin mà người dùng đã nhập vào Form. Như vậy AppUserValidator sẽ phê chuẩn (validate) giá trị các trường (field) của đối tượng AppUserForm.
AppUserValidator.java
package org.o7planning.sbformvalidation.validator;
import org.apache.commons.validator.routines.EmailValidator;
import org.o7planning.sbformvalidation.dao.AppUserDAO;
import org.o7planning.sbformvalidation.formbean.AppUserForm;
import org.o7planning.sbformvalidation.model.AppUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
@Component
public class AppUserValidator implements Validator {
// common-validator library.
private EmailValidator emailValidator = EmailValidator.getInstance();
@Autowired
private AppUserDAO appUserDAO;
// Các lớp được hỗ trợ bởi Validator này.
@Override
public boolean supports(Class<?> clazz) {
return clazz == AppUserForm.class;
}
@Override
public void validate(Object target, Errors errors) {
AppUserForm appUserForm = (AppUserForm) target;
// Kiểm tra các field của AppUserForm.
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "userName", "NotEmpty.appUserForm.userName");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "NotEmpty.appUserForm.firstName");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", "NotEmpty.appUserForm.lastName");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", "NotEmpty.appUserForm.email");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "password", "NotEmpty.appUserForm.password");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "confirmPassword", "NotEmpty.appUserForm.confirmPassword");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "gender", "NotEmpty.appUserForm.gender");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "countryCode", "NotEmpty.appUserForm.countryCode");
if (!this.emailValidator.isValid(appUserForm.getEmail())) {
// Email không hợp lệ.
errors.rejectValue("email", "Pattern.appUserForm.email");
} else if (appUserForm.getUserId() == null) {
AppUser dbUser = appUserDAO.findAppUserByEmail(appUserForm.getEmail());
if (dbUser != null) {
// Email đã được sử dụng bởi tài khoản khác.
errors.rejectValue("email", "Duplicate.appUserForm.email");
}
}
if (!errors.hasFieldErrors("userName")) {
AppUser dbUser = appUserDAO.findAppUserByUserName(appUserForm.getUserName());
if (dbUser != null) {
// Tên tài khoản đã bị sử dụng bởi người khác.
errors.rejectValue("userName", "Duplicate.appUserForm.userName");
}
}
if (!errors.hasErrors()) {
if (!appUserForm.getConfirmPassword().equals(appUserForm.getPassword())) {
errors.rejectValue("confirmPassword", "Match.appUserForm.confirmPassword");
}
}
}
}
7. Controller
MainController.java
package org.o7planning.sbformvalidation.controller;
import java.util.List;
import org.o7planning.sbformvalidation.dao.AppUserDAO;
import org.o7planning.sbformvalidation.dao.CountryDAO;
import org.o7planning.sbformvalidation.formbean.AppUserForm;
import org.o7planning.sbformvalidation.model.AppUser;
import org.o7planning.sbformvalidation.model.Country;
import org.o7planning.sbformvalidation.validator.AppUserValidator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
// import org.springframework.transaction.annotation.Transactional;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
@Controller
public class MainController {
@Autowired
private AppUserDAO appUserDAO;
@Autowired
private CountryDAO countryDAO;
@Autowired
private AppUserValidator appUserValidator;
// Set a form validator
@InitBinder
protected void initBinder(WebDataBinder dataBinder) {
// Form mục tiêu
Object target = dataBinder.getTarget();
if (target == null) {
return;
}
System.out.println("Target=" + target);
if (target.getClass() == AppUserForm.class) {
dataBinder.setValidator(appUserValidator);
}
// ...
}
@RequestMapping("/")
public String viewHome(Model model) {
return "welcomePage";
}
@RequestMapping("/members")
public String viewMembers(Model model) {
List<AppUser> list = appUserDAO.getAppUsers();
model.addAttribute("members", list);
return "membersPage";
}
@RequestMapping("/registerSuccessful")
public String viewRegisterSuccessful(Model model) {
return "registerSuccessfulPage";
}
// Hiển thị trang đăng ký.
@RequestMapping(value = "/register", method = RequestMethod.GET)
public String viewRegister(Model model) {
AppUserForm form = new AppUserForm();
List<Country> countries = countryDAO.getCountries();
model.addAttribute("appUserForm", form);
model.addAttribute("countries", countries);
return "registerPage";
}
// Phương thức này được gọi để lưu thông tin đăng ký.
// @Validated: Để đảm bảo rằng Form này
// đã được Validate trước khi phương thức này được gọi.
@RequestMapping(value = "/register", method = RequestMethod.POST)
public String saveRegister(Model model, //
@ModelAttribute("appUserForm") @Validated AppUserForm appUserForm, //
BindingResult result, //
final RedirectAttributes redirectAttributes) {
// Validate result
if (result.hasErrors()) {
List<Country> countries = countryDAO.getCountries();
model.addAttribute("countries", countries);
return "registerPage";
}
AppUser newUser= null;
try {
newUser = appUserDAO.createAppUser(appUserForm);
}
// Other error!!
catch (Exception e) {
List<Country> countries = countryDAO.getCountries();
model.addAttribute("countries", countries);
model.addAttribute("errorMessage", "Error: " + e.getMessage());
return "registerPage";
}
redirectAttributes.addFlashAttribute("flashUser", newUser);
return "redirect:/registerSuccessful";
}
}
8. Thymeleaf Template
_menu.html
<div xmlns:th="http://www.thymeleaf.org"
style="border: 1px solid #ccc;padding:5px;margin-bottom:20px;">
<a th:href="@{/}">Home</a>
|
<a th:href="@{/members}">Members</a>
|
<a th:href="@{/register}">Register</a>
</div>
membersPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:utext="${title}"></title>
</head>
<style>
table th, table td {
padding: 5px;
}
.message {
color: blue;
}
</style>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>Members</h2>
<table border="1">
<tr>
<th>User Name</th>
<th>First Name</th>
<th>Last Name</th>
<th>Email</th>
<th>Gender</th>
</tr>
<tr th:each ="member : ${members}">
<td th:utext="${member.userName}">...</td>
<td th:utext="${member.firstName}">...</td>
<td th:utext="${member.lastName}">...</td>
<td th:utext="${member.email}">...</td>
<td th:utext="${member.gender}">...</td>
</tr>
</table>
</body>
</html>
registerPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:utext="${title}"></title>
<style>
th, td {
padding: 5px;
}
td span {
font-size:90%;
font-style: italic;
color: red;
}
.error {
color: red;
font-style: italic;
}
</style>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>Register</h2>
<div th:if="${errorMessage != null}"
th:utext="${errorMessage}" class="error">...</div>
<form th:action="@{/register}" th:object="${appUserForm}" method="POST">
<table>
<tr>
<td>User Name</td>
<td><input type="text" th:field="*{userName}" /></td>
<td>
<span th:if="${#fields.hasErrors('userName')}" th:errors="*{userName}">..</span>
</td>
</tr>
<tr>
<td>Password</td>
<td><input type="password" th:field="*{password}" /> </td>
<td>
<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}">..</span>
</td>
</tr>
<tr>
<td>Confirm</td>
<td><input type="password" th:field="*{confirmPassword}" /> </td>
<td>
<span th:if="${#fields.hasErrors('confirmPassword')}" th:errors="*{confirmPassword}">..</span>
</td>
</tr>
<tr>
<td>Email</td>
<td><input type="text" th:field="*{email}" /> </td>
<td>
<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}">..</span>
</td>
</tr>
<tr>
<td>First Name</td>
<td><input type="text" th:field="*{firstName}" /> </td>
<td>
<span th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}">..</span>
</td>
</tr>
<tr>
<td>Last Name</td>
<td><input type="text" th:field="*{lastName}" /> </td>
<td>
<span th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}">..</span>
</td>
</tr>
<tr>
<td>Gender</td>
<td>
<select th:field="*{gender}">
<option value=""> -- </option>
<option value="M">Male</option>
<option value="F">Female</option>
</select>
</td>
<td>
<span th:if="${#fields.hasErrors('gender')}" th:errors="*{gender}">..</span>
</td>
</tr>
<tr>
<td>Country</td>
<td>
<select th:field="*{countryCode}">
<option value=""> -- </option>
<option th:each="country : ${countries}"
th:value="${country.countryCode}"
th:utext="${country.countryName}"/>
</select>
<td><span th:if="${#fields.hasErrors('countryCode')}" th:errors="*{countryCode}">..</span></td>
</tr>
<tr>
<td> </td>
<td>
<input type="submit" value="Submit" />
<a th:href="@{/}">Cancel</a>
</td>
<td> </td>
</tr>
</table>
</form>
</body>
</html>
registerSuccessfulPage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Successfully registered</title>
<style>
span {color: blue;}
</style>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>You have successfully registered!</h2>
<div th:if="${flashUser != null}">
<ul>
<li>User Name: <span th:utext="${flashUser.userName}">..</span></li>
<li>Email: <span th:utext="${flashUser.email}">..</span></li>
</ul>
</div>
</body>
</html>
welcomePage.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title th:utext="${title}"></title>
</head>
<body>
<!-- Include _menu.html -->
<th:block th:include="/_menu"></th:block>
<h2>Home Page!</h2>
</body>
</html>
Các hướng dẫn Spring Boot
- Cài đặt Spring Tool Suite cho Eclipse
- Hướng dẫn lập trình Spring cho người mới bắt đầu
- Hướng dẫn lập trình Spring Boot cho người mới bắt đầu
- Các thuộc tính thông dụng của Spring Boot
- Hướng dẫn sử dụng Spring Boot và Thymeleaf
- Hướng dẫn sử dụng Spring Boot và FreeMarker
- Hướng dẫn sử dụng Spring Boot và Groovy
- Hướng dẫn sử dụng Spring Boot và Mustache
- Hướng dẫn sử dụng Spring Boot và JSP
- Hướng dẫn sử dụng Spring Boot, Apache Tiles, JSP
- Sử dụng Logging trong Spring Boot
- Giám sát ứng dụng với Spring Boot Actuator
- Tạo ứng dụng web đa ngôn ngữ với Spring Boot
- Sử dụng nhiều ViewResolver trong Spring Boot
- Sử dụng Twitter Bootstrap trong Spring Boot
- Hướng dẫn và ví dụ Spring Boot Interceptor
- Hướng dẫn sử dụng Spring Boot, Spring JDBC và Spring Transaction
- Hướng dẫn và ví dụ Spring JDBC
- Hướng dẫn sử dụng Spring Boot, JPA và Spring Transaction
- Hướng dẫn sử dụng Spring Boot và Spring Data JPA
- Hướng dẫn sử dụng Spring Boot, Hibernate và Spring Transaction
- Tương tác Spring Boot, JPA và cơ sở dữ liệu H2
- Hướng dẫn sử dụng Spring Boot và MongoDB
- Sử dụng nhiều DataSource với Spring Boot và JPA
- Sử dụng nhiều DataSource với Spring Boot và RoutingDataSource
- Tạo ứng dụng Login với Spring Boot, Spring Security, Spring JDBC
- Tạo ứng dụng Login với Spring Boot, Spring Security, JPA
- Tạo ứng dụng đăng ký tài khoản với Spring Boot, Spring Form Validation
- Ví dụ OAuth2 Social Login trong Spring Boot
- Chạy các nhiệm vụ nền theo lịch trình trong Spring
- Ví dụ CRUD Restful Web Service với Spring Boot
- Ví dụ Spring Boot Restful Client với RestTemplate
- Ví dụ CRUD với Spring Boot, REST và AngularJS
- Bảo mật Spring Boot RESTful Service sử dụng Basic Authentication
- Bảo mật Spring Boot RESTful Service sử dụng Auth0 JWT
- Ví dụ Upload file với Spring Boot
- Ví dụ Download file với Spring Boot
- Ví dụ Upload file với Spring Boot và jQuery Ajax
- Ví dụ Upload file với Spring Boot và AngularJS
- Tạo ứng dụng Web bán hàng với Spring Boot, Hibernate
- Hướng dẫn và ví dụ Spring Email
- Tạo ứng dụng Chat đơn giản với Spring Boot và Websocket
- Triển khai ứng dụng Spring Boot trên Tomcat Server
- Triển khai ứng dụng Spring Boot trên Oracle WebLogic Server
- Cài đặt chứng chỉ SSL miễn phí Let's Encrypt cho Spring Boot
- Cấu hình Spring Boot chuyển hướng HTTP sang HTTPS
- Tìm nạp dữ liệu với Spring Data JPA DTO Projections
Show More