CÔNG NGHỆ THÔNG TIN >> SINH VIÊN BKAP

Validation Form-Upload File Image-Nhúng CKEditor trong SpringBoot

Đăng lúc: 10:28 AM - 19/03/2025 bởi Charles Chung - 336

Trong bài này tôi sẽ hướng dẫn các bạn kiểm tra tính hợp lệ của dữ liệu nhập trên form, upload tệp tin hình ảnh, nhúng CKEditor vào trong SpringBoot

1. Giới thiệu

Validation dữ liệu: là quá trình kiểm tra tính hợp lệ của dữ liệu gửi từ người dùng đến ứng dụng Web.

Trong Thymeleaf, validation form (xác thực biểu mẫu) giúp đảm bảo rằng dữ liệu người dùng nhập vào là hợp lệ trước khi gửi đến server. Validation có thể thực hiện trên cả phía client (bằng JavaScript) và phía server (thông qua các anotations trong Spring như @NotNull, @Size, v.v.). Thymeleaf hỗ trợ việc hiển thị thông báo lỗi một cách dễ dàng khi kết hợp với Spring Validation, giúp người dùng nhận được phản hồi về các trường hợp nhập liệu không hợp lệ.

  • Để sử dụng SpringBoot validation các bạn cần bổ sung thêm dependency sau:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

  • Trong phương thức Post của controller nhận dữ liệu từ Form chúng ta cần khai báo @Valid cho tham số model để xác nhận validate và sử dụng thêm tham số kiểu BindingResult để nhận kết quả validate.
  • Trên view khi cần in lỗi 1 trường chúng ta sử dụng

<td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td>

2. Ví dụ Validation Form

Trong ví dụ này chúng ta sẽ

  • Tạo Form đăng ký người dùng
  • Validate các trường dữ liệu trên form bằng cách sử dụng các Annotation trong Java

Bước 1: Tạo project Springboot session5example1 (xem bài 1), nhớ chọn thêm dependency validation

Bước 2: Tạo các package, class, view với cấu trúc như hình dưới

Bước 3: Code lớp User (biểu diễn thông tin đăng ký)

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
package com.bkap.entities;

import java.util.Date;



import org.springframework.format.annotation.DateTimeFormat;

import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;


public class User {
	@Size(min = 3, max = 50, message = "Độ dài không hợp lệ 5-50")
	private String name;
	
	@NotBlank(message = "Hãy nhập email")
	@Email(message = "Email phải đúng định dạng")
	private String email;

	@Size(min = 8, max = 15, message = "Độ dài không hợp lệ 8-15")
	private String password;
	
	@NotBlank(message = "Giới tính không để trống")
	private String gender;
	
	@Size(max = 100, message = "Độ dài tối đa 100 ký tự")
	private String note;
	
	@AssertTrue(message = "Hãy chọn có gia đình")
	private boolean married;
	
	@DateTimeFormat(pattern = "dd/MM/yyyy")
	private Date birthday;
	
	@Min(value = 20_000,message = "Tối thiểu 20000")
	@Max(value = 200_000,message = "Tối đa 200000")
	private long income;
	
	@NotBlank(message = "Nghề nghiệp không để trống")
	private String profession;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public String getNote() {
		return note;
	}
	public void setNote(String note) {
		this.note = note;
	}
	public boolean isMarried() {
		return married;
	}
	public void setMarried(boolean married) {
		this.married = married;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	public long getIncome() {
		return income;
	}
	public void setIncome(long income) {
		this.income = income;
	}
	public String getProfession() {
		return profession;
	}
	public void setProfession(String profession) {
		this.profession = profession;
	}
	
}

Bước 4: Code lớp HomeController

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package com.bkap.controllers;

import java.util.Arrays;
import java.util.List;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.bkap.entities.User;

import jakarta.validation.Valid;

@Controller
public class HomeController {

	@GetMapping({"/","/register"})
	public String showForm(Model model) {
		var user = new User();
		List<String> listprofession = Arrays.asList("Developer", "Tester", "Architect", "Designer");
		model.addAttribute("user", user);
		model.addAttribute("listprofession", listprofession);
		return "register";
	}

	@PostMapping("/register")
	public String register(@Valid User user,  BindingResult result,Model model) {
		if (result.hasErrors()) {
			List<String> listprofession = Arrays.asList("Developer", "Tester", "Architect", "Designer");
			model.addAttribute("user", user);
			model.addAttribute("listprofession", listprofession);
			return "register";
		} else {
			model.addAttribute("user", user);
			return "success";
		}
	}
}

Bước 5: Code tệp register.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Đăng ký thành viên</title>
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" />
</head>
<body>
	<div class="container">
		<h1>ĐĂNG KÝ THÀNH VIÊN - Kiểm tra dữ liệu hợp lệ</h1>
		<form action="#" th:action="@{/register}" method="post"
			th:object="${user}">
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="name">Họ và tên</label>
				<div class="col-sm-10">
					<input type="text" th:field="*{name}" class="form-control" />
					<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="text-danger">Full name error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="email">E-mail:</label>
				<div class="col-sm-10">
					<input type="text" class="form-control" th:field="*{email}" />
					<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="text-danger">Email error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="password">Mật
					khẩu:</label>
				<div class="col-sm-10">
					<input type="password" th:field="*{password}" class="form-control" />
					<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="text-danger">Password error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="birthday">Ngày
					sinh (d/M/y):</label>
				<div class="col-sm-10">
					<input type="text" class="form-control" th:field="*{birthday}" />
					<span th:if="${#fields.hasErrors('birthday')}" th:errors="*{birthday}" class="text-danger">Birthday error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="gender">Giới
					tính:</label>
				<div class="col-sm-10">
					<input type="radio" th:field="*{gender}" value="Male" /> Male <input
						type="radio" th:field="*{gender}" value="Female" /> Female
					<span th:if="${#fields.hasErrors('gender')}" th:errors="*{gender}" class="text-danger">Gender error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="profession">Nghề
					nghiệp:</label>
				<div class="col-sm-10">
					<select class="form-control" th:field="*{profession}">
						<option th:each="p:${listprofession}" th:value="${p}"
							th:text="${p}" />
					</select>
					<span th:if="${#fields.hasErrors('income')}" th:errors="*{profession}" class="text-danger">Profession error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="married">Có gia
					đình?</label>
				<div class="col-sm-10">
					<input type="checkbox" th:field="*{married}" />
					<span th:if="${#fields.hasErrors('married')}" th:errors="*{married}" class="text-danger">Married error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="income">Thu nhập:</label>
				<div class="col-sm-10">
					<input type="text" class="form-control" th:field="*{income}" />
					<span th:if="${#fields.hasErrors('income')}" th:errors="*{income}" class="text-danger">Birthday error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="note">Ghi chú:</label>
				<div class="col-sm-10">
					<textarea rows="5" cols="25" th:field="*{note}"
						class="form-control"></textarea>
						<span th:if="${#fields.hasErrors('note')}" th:errors="*{note}" class="text-danger">Note error</span>
				</div>
			</div>
			<button class="btn btn-primary" type="submit">Đăng ký</button>
		</form>
	</div>
</body>
</html>

Bước 6: Code tệp success.html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Đăng ký thành công</title>
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" />
</head>
<body>
	<div class="container">
		<h1>Thông tin đăng ký</h1>
		<div th:object="${user}">
			<p>
				Họ và tên: <span th:text="*{name}"></span>
			</p>
			<p>
				Email: <span th:text="*{email}"></span>
			</p>
			<p>
				Mật khẩu: <span>*******</span>
			</p>
			<p>
				Giới tính: <span th:text="*{gender}"></span>
			</p>
			<p>
				Ngày sinh: <span th:text="*{#dates.format(birthday, 'dd-MM-yyyy')}"></span>
			</p>
			<p>
				Nghề nghiệp: <span th:text="*{profession}"></span>
			</p>
			<p>
				Ghi chú: <span th:text="*{note}"></span>
			</p>
			<p>
				Thu nhập: <span th:text="*{income}"></span>
			</p>
			<a href="/">Quay lại</a>
		</div>
	</div>
</body>
</html>

Bước 7 Chạy và xem kết quả

 

Source code tải tại đây

3. Custome error message

Để thuận tiện cho việc sửa đổi thông báo lỗi và đa ngôn ngữ, chúng ta nên định  nghĩa các thông báo lỗi vào tệp tin properties, các bước điều chỉnh lại bài trên  như sau:

Bước 1: Bổ sung thêm tệp validation.properties vào thưc mục src/main/resource với nội dung sau:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# customize error message
name.size=Độ dài không hợp lệ 3-50
email.message = Email không hợp lệ"
email.notblank=Email không để trống
password.size=Độ dài không hợp lệ 8-15
gender.notblank=Giới tính không để trống
node.size=Độ dài tối đa 100 ký tự
income.min=Tối thiểu 20000
incom.max=Tối đa 200000
profession.notblank=Nghề nghiệp không để trống
married.istrue=Hãy chọn có gia đình

Bước 2: Tạo gói com.bkap.validationconfig sau đó tạo lớp cấu hình ValidationConfig nội dung sau:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package com.bkap.validationconfig;

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;

@Configuration
public class ValidationConfig {
	@Bean
    public MessageSource messageSource() {
        ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource();
        messageSource.setBasename("classpath:validation");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
}

Bước 3: Sửa lại code lớp User như sau:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
package com.bkap.entities;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;


public class User {
	@Size(min = 3, max = 50, message = "{name.size}")
	private String name;
	
	@NotBlank(message = "{email.notblank}")
	@Email(message = "{email.message}")
	private String email;

	@Size(min = 8, max = 15, message = "{password.size}")
	private String password;
	
	@NotBlank(message = "{gender.notblank}")
	private String gender;
	
	@Size(max = 100, message = "{note.size}")
	private String note;
	
	@AssertTrue(message = "{married.istrue}")
	private boolean married;
	
	@DateTimeFormat(pattern = "dd/MM/yyyy")
	private Date birthday;
	
	@Min(value = 20_000,message = "{income.min}")
	@Max(value = 200_000,message = "{income.max}")
	private long income;
	
	@NotBlank(message = "{profession.notblank}")
	private String profession;
	
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public String getNote() {
		return note;
	}
	public void setNote(String note) {
		this.note = note;
	}
	public boolean isMarried() {
		return married;
	}
	public void setMarried(boolean married) {
		this.married = married;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	public long getIncome() {
		return income;
	}
	public void setIncome(long income) {
		this.income = income;
	}
	public String getProfession() {
		return profession;
	}
	public void setProfession(String profession) {
		this.profession = profession;
	}
	
}

Bước 4: Chạy và kiểm tra kết quả như bài trên

Source code tải tại đây

4. Phát triển tiếp bài phía trên để nhúng CKEditor và Upload Ảnh

Bước 1: Tải ckeditor tại đây, giải nén và copy và thư mục src/main/resource

Bước 2: Bổ sung thêm dependencies và file pom.xml (nếu dung spring mvc)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/commons-io/commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.18.0</version>
</dependency>

Bước 3: Bổ sung thêm trường avatar vào lớp User

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
package com.bkap.entities;

import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;

import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;


public class User {
	@Size(min = 3, max = 50, message = "{name.size}")
	private String name;
	
	@NotBlank(message = "{email.notblank}")
	@Email(message = "{email.message}")
	private String email;

	@Size(min = 8, max = 15, message = "{password.size}")
	private String password;
	
	@NotBlank(message = "{gender.notblank}")
	private String gender;
	
	@NotBlank(message = "{note.notblank}")
	private String note;
	
	@AssertTrue(message = "{married.istrue}")
	private boolean married;
	
	@DateTimeFormat(pattern = "dd/MM/yyyy")
	private Date birthday;
	
	@Min(value = 20_000,message = "{income.min}")
	@Max(value = 200_000,message = "{income.max}")
	private long income;
	
	@NotBlank(message = "{profession.notblank}")
	private String profession;
	
	private String avatar;
	
	
	public String getAvatar() {
		return avatar;
	}
	public void setAvatar(String avatar) {
		this.avatar = avatar;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	public String getNote() {
		return note;
	}
	public void setNote(String note) {
		this.note = note;
	}
	public boolean isMarried() {
		return married;
	}
	public void setMarried(boolean married) {
		this.married = married;
	}
	public Date getBirthday() {
		return birthday;
	}
	public void setBirthday(Date birthday) {
		this.birthday = birthday;
	}
	public long getIncome() {
		return income;
	}
	public void setIncome(long income) {
		this.income = income;
	}
	public String getProfession() {
		return profession;
	}
	public void setProfession(String profession) {
		this.profession = profession;
	}
	
}

Bước 4: Bổ sung thêm code vào phương thức register của HomeController như sau:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.bkap.controllers;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.bkap.entities.User;

import jakarta.validation.Valid;

@Controller
public class HomeController {


	@GetMapping({"/","/register"})
	public String showForm(Model model) {
		var user = new User();
		List<String> listprofession = Arrays.asList("Developer", "Tester", "Architect", "Designer");
		model.addAttribute("user", user);
		model.addAttribute("listprofession", listprofession);
		return "register";
	}

	@PostMapping("/register")
	public String register(@Valid User user, BindingResult result, @RequestParam MultipartFile file, Model model) {
		if (result.hasErrors()) {
			List<String> listprofession = Arrays.asList("Developer", "Tester", "Architect", "Designer");
			model.addAttribute("user", user);
			model.addAttribute("listprofession", listprofession);
			return "register";
		} else {
			try {
				// upload ảnh
				if (!file.isEmpty()) {
					File imageFolder=new File( new ClassPathResource(".").getFile()+"/static/images");
					if(!imageFolder.exists())
						imageFolder.mkdir();
					Path path=Paths.get(imageFolder.getAbsolutePath(),file.getOriginalFilename());
					System.out.println(path);
					file.transferTo(path);
					user.setAvatar("/images/"+file.getOriginalFilename());
				}
			} catch (IllegalStateException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			model.addAttribute("user", user);
			return "success";
		}
	}
}

Bước 5: Sửa lại code tệp register.html như sau

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Đăng ký thành viên</title>
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" />

</head>
<body>
	<div class="container">
		<h1>ĐĂNG KÝ THÀNH VIÊN - CKEditor và Upload ảnh</h1>
		<form action="#" th:action="@{/register}" method="post" enctype="multipart/form-data"
			th:object="${user}">
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="name">Họ và tên</label>
				<div class="col-sm-10">
					<input type="text" th:field="*{name}" class="form-control" />
					<span th:if="${#fields.hasErrors('name')}" th:errors="*{name}" class="text-danger">Full name error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="email">E-mail:</label>
				<div class="col-sm-10">
					<input type="text" class="form-control" th:field="*{email}" />
					<span th:if="${#fields.hasErrors('email')}" th:errors="*{email}" class="text-danger">Email error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="password">Mật
					khẩu:</label>
				<div class="col-sm-10">
					<input type="password" th:field="*{password}" class="form-control" />
					<span th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="text-danger">Password error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="birthday">Ngày
					sinh (d/M/y):</label>
				<div class="col-sm-10">
					<input type="text" class="form-control" th:field="*{birthday}" />
					<span th:if="${#fields.hasErrors('birthday')}" th:errors="*{birthday}" class="text-danger">Birthday error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="gender">Giới
					tính:</label>
				<div class="col-sm-10">
					<input type="radio" th:field="*{gender}" value="Male" /> Male <input
						type="radio" th:field="*{gender}" value="Female" /> Female
					<span th:if="${#fields.hasErrors('gender')}" th:errors="*{gender}" class="text-danger">Gender error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="profession">Nghề
					nghiệp:</label>
				<div class="col-sm-10">
					<select class="form-control" th:field="*{profession}">
						<option th:each="p:${listprofession}" th:value="${p}"
							th:text="${p}" />
					</select>
					<span th:if="${#fields.hasErrors('income')}" th:errors="*{profession}" class="text-danger">Profession error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="married">Có gia
					đình?</label>
				<div class="col-sm-10">
					<input type="checkbox" th:field="*{married}" />
					<span th:if="${#fields.hasErrors('married')}" th:errors="*{married}" class="text-danger">Married error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="income">Thu nhập:</label>
				<div class="col-sm-10">
					<input type="text" class="form-control" th:field="*{income}" />
					<span th:if="${#fields.hasErrors('income')}" th:errors="*{income}" class="text-danger">Birthday error</span>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="file">Ảnh:</label>
				<div class="col-sm-10">
					<input type="file" name="file" class="form-control" accept="image/*"/>
				</div>
			</div>
			<div class="form-group row">
				<label class="col-sm-2 col-form-label" for="note">Ghi chú:</label>
				<div class="col-sm-10">
					<textarea rows="5" cols="25" th:field="*{note}"
						class="form-control"></textarea>
						<span th:if="${#fields.hasErrors('note')}" th:errors="*{note}" class="text-danger">Note error</span>
				</div>
			</div>
			<button class="btn btn-primary" type="submit">Đăng ký</button>
		</form>
		</div>
		<script src="/ckeditor/ckeditor.js"></script>
	<script>

CKEDITOR.replace('note')

</script>
</body>
</html>

Bước 6: Sửa lại code tệp success.html như sau

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Đăng ký thành công</title>
<link rel="stylesheet"
	href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.2/dist/css/bootstrap.min.css" />
</head>
<body>
	<div class="container">
	<h1>Thông tin đăng ký</h1>
		<div th:object="${user}">
			<p>
				Họ và tên: <span th:text="*{name}"></span>
			</p>
			<p>
				Email: <span th:text="*{email}"></span>
			</p>
			<p>
				Mật khẩu: <span>*******</span>
			</p>
			<p>
				Giới tính: <span th:text="*{gender}"></span>
			</p>
			<p>
				Ngày sinh: <span th:text="*{#dates.format(birthday, 'dd-MM-yyyy')}"></span>
			</p>
			<p>
				Nghề nghiệp: <span th:text="*{profession}"></span>
			</p>
			<p>
				Ghi chú: <span th:utext="*{note}"></span>
			</p>
			<p>
				Thu nhập: <span th:text="*{income}"></span>
			</p>
			<p>
				Ảnh: <img th:src="*{avatar}" width="100" /></p> <a href="/">Quay lại</a>
		</div>
	</div>
</body>
</html>

Bước 7: Chạy và xem kết quả

Source code tải tại đây

5. Video demo (Quay trong buổi dạy lớp C2308G)

thay lời cảm ơn!

QUẢNG CÁO - TIẾP THỊ