hocvietcode.com
  • Trang chủ
  • Học lập trình
    • Lập trình C/C++
    • Cấu trúc dữ liệu và giải thuật
    • Lập trình HTML
    • Lập trình Javascript
      • Javascript cơ bản
      • ReactJS framework
      • AngularJS framework
      • Typescript cơ bản
      • Angular
      • NodeJS
    • Lập trình Mobile
      • Lập Trình Dart Cơ Bản
        • Dart Flutter Framework
    • Cơ sở dữ liệu
      • MySQL – MariaDB
      • Micrsoft SQL Server
      • Extensible Markup Language (XML)
      • JSON
      • MongoDB
    • Lập trình PHP
      • Lập trình PHP cơ bản
      • Laravel Framework
    • Lập trình Java
      • Java Cơ bản
      • Spring Boot
    • Lập trình C#
      • Lập Trình C# Cơ Bản
      • ASP.NET Core MVC
    • Lập trình Python
    • Git và Github
    • Machine Learning
  • WORDPRESS
    • WordPress cơ bản
    • WordPress nâng cao
    • Chia sẻ WordPress
  • Kiến thức hệ thống
    • Microsoft Azure
    • Docker
    • Linux
    • Mạng và truyền thông
  • Chia sẻ IT
    • Tin học văn phòng
      • Microsoft Word
      • Microsoft Excel
    • Marketing
      • Google Adwords
      • Facebook Ads
      • Kiến thức khác
    • Chia sẻ phần mềm
    • Review công nghệ
    • Công cụ – tiện ích
      • Kiểm tra bàn phím online
      • Kiểm tra webcam online
Đăng nhập
  • Đăng nhập / Đăng ký

Please enter key search to display results.

Home
  • Spring Boot
Spring Boot bài 06: Hiển thị chi tiết khóa học bằng Thymeleaf
Java và Spring Boot – MVC và REST API

Spring Boot bài 06: Hiển thị chi tiết khóa học bằng Thymeleaf

  • 18-10-2026
  • Toanngo92
  • 0 Comments

Giáo trình Spring Boot MVC và REST API · Chương 3/12: Spring MVC và dữ liệu hiển thị · Bài 06/28 · Biên soạn tháng 10/2026.

← Bài 05 · Mục lục 12 chương · Bài 07 →

Mục lục

  • Mục tiêu bài học
  • Chọn dữ liệu theo id
  • Template đọc DTO ra sao?
  • HTTP status và giao diện phải cùng phản ánh kết quả
  • Kiểm tra kết quả với dữ liệu có ý nghĩa
  • Lỗi thường gặp và lưu ý
  • Ghi nhớ
  • Tài liệu đối chiếu

Mục tiêu bài học

Đọc dữ liệu từ Model, tạo liên kết chi tiết và trả trang 404 khi tài nguyên không tồn tại.

Chọn dữ liệu theo id

Trang danh sách chứa nhiều khóa học, còn trang chi tiết chỉ hiển thị một khóa học xác định. URL /courses/1 truyền id cho Controller; Controller gọi Service rồi đặt DTO vào Model với tên course. Dữ liệu hiển thị được quyết định phía server, không lấy id từ một ô ẩn rồi tin rằng đó là khóa học hợp lệ.

Phương thức Service tìm entity hoặc ném CourseNotFound. Việc trả null rồi để template truy cập thuộc tính sẽ biến một tình huống bình thường thành lỗi render khó hiểu. Tách lỗi không tồn tại giúp giao diện và API đều có thể trả 404 theo cách phù hợp.

Tệp trong mã nguồn mẫu: src/main/java/com/hocvietcode/academy/CourseWebController.java. Đoạn dưới được trích từ tệp này; xem bản tải về để có đủ import và phần còn lại.

@GetMapping("/courses/{id}") String detail(@PathVariable Long id,Model model){model.addAttribute("course",service.get(id));return "courses/detail";}

Template đọc DTO ra sao?

${course.title} đọc thuộc tính title của đối tượng trong Model. th:text thay nội dung của phần tử bằng văn bản đã escape. th:href tạo URL từ biểu thức, thay id và xử lý tham số cho đúng. Không nên nối tay URL chứa dữ liệu có khoảng trắng hoặc ký tự đặc biệt.

Trong bản mẫu, CourseDto là record có id, title, price, categoryId và categoryName. DTO không mang theo các quan hệ JPA tùy ý hoặc thông tin nhạy cảm. Đây là cách giới hạn rõ ràng những gì trang được phép hiển thị và cũng là bước chuẩn bị để dùng cùng dữ liệu trong API JSON.

<h1 th:text="${course.title}">Tên khóa học mẫu</h1>
<p>Danh mục: <span th:text="${course.categoryName}"></span></p>
<p>Giá: <span th:text="${course.price}"></span></p>
<a th:href="@{/courses/{id}(id=${course.id})}">Xem chi tiết</a>

HTTP status và giao diện phải cùng phản ánh kết quả

Một trang có dòng “Không tìm thấy” nhưng trả 200 vẫn thông báo sai cho client và công cụ tìm kiếm. WebErrors vừa đặt status 404 vừa chọn template error/custom. Trình duyệt hiển thị thông báo thân thiện; Network vẫn cho biết tài nguyên không tồn tại.

Advice này được giới hạn cho Controller giao diện và tệp. API dùng advice riêng để trả ProblemDetail, không trả HTML cho client đang mong JSON. Không gom mọi exception thành 200 kèm thông báo; cũng không gửi stack trace hoặc câu SQL ra cho người xem.

Tệp trong mã nguồn mẫu: src/main/java/com/hocvietcode/academy/WebErrors.java. Đoạn dưới được trích từ tệp này; xem bản tải về để có đủ import và phần còn lại.

package com.hocvietcode.academy;
import org.springframework.web.bind.annotation.*;
import org.springframework.ui.Model;
import jakarta.servlet.http.HttpServletResponse;
@ControllerAdvice(assignableTypes={CourseWebController.class,FileController.class})
public class WebErrors {
    @ExceptionHandler(CourseNotFound.class) String missing(Model model,HttpServletResponse response){response.setStatus(404);model.addAttribute("message","Không tìm thấy khóa học");return "error/custom";}
    @ExceptionHandler(IllegalArgumentException.class) String invalid(IllegalArgumentException e,Model model,HttpServletResponse response){response.setStatus(400);model.addAttribute("message",e.getMessage());return "error/custom";}
}

Kiểm tra kết quả với dữ liệu có ý nghĩa

Mở /courses/1 để thấy tên và danh mục từ dữ liệu seed. Mở /courses/999999 để kiểm tra 404. Nếu giá là 0, đó là giá trị BigDecimal hợp lệ của khóa miễn phí, không phải dữ liệu bị thiếu. Việc trình bày 0 thành “Miễn phí” có thể làm ở template, còn giá trị nghiệp vụ vẫn giữ dạng số.

Trang detail trong mã mẫu có liên kết sửa và form xóa để minh họa luồng quản trị. Server mới là nơi quyết định quyền: một người nhìn thấy liên kết không đồng nghĩa được phép thực hiện. Ở bài bảo mật, ta kiểm tra cả route quản trị và request ghi dữ liệu, thay vì chỉ ẩn nút trên giao diện.

Lỗi thường gặp và lưu ý

  • Không dùng th:utext cho tiêu đề người dùng nhập vì nó render HTML không escape.
  • Không sửa template để che mọi lỗi lazy loading; cần chuẩn bị DTO trong transaction ở Service.

Ghi nhớ

Trang chi tiết cần một DTO đúng phạm vi, URL rõ ràng, hiển thị an toàn và mã HTTP phản ánh đúng kết quả.

Tài liệu đối chiếu

  • Thymeleaf + Spring

← Bài 05 · Mục lục 12 chương · Bài 07 →

Bài viết liên quan:

Java và Spring Boot – MVC và REST API
Spring Boot bài 23: Gọi dịch vụ HTTP bằng RestClient và đặt timeout
Java và Spring Boot – MVC và REST API
Spring Boot bài 08: Fragment, layout và tài nguyên CSS trong Spring Boot
Java và Spring Boot – MVC và REST API
Spring Boot bài 24: Gửi email và lập lịch tác vụ trong Spring Boot
Java và Spring Boot – MVC và REST API
Spring Boot bài 09: Binding biểu mẫu tạo và sửa khóa học
Java và Spring Boot – MVC và REST API
Spring Boot bài 25: Unit test Service với JUnit và Mockito
Java và Spring Boot – MVC và REST API
Spring Boot bài 10: Validation và BindingResult: báo lỗi ngay trên biểu mẫu
Java và Spring Boot – MVC và REST API
Spring Boot bài 26: Integration test MVC, API, database và Security
Java và Spring Boot – MVC và REST API
Spring Boot bài 11: Post/Redirect/Get, flash message và session
Java và Spring Boot – MVC và REST API
Spring Boot bài 27: Logging, Actuator và quản lý schema bằng Flyway
Java và Spring Boot – MVC và REST API
Spring Boot bài 12: JPA Entity, Repository, H2 và migration đầu tiên
Java và Spring Boot – MVC và REST API
Spring Boot bài 28: Đóng gói JAR, Docker và cấu hình triển khai
Java và Spring Boot – MVC và REST API
Spring Boot bài 13: Kết nối MySQL, CRUD và transaction trong Service

Dịch vụ thiết kế Wesbite

NỘI DUNG MỚI CẬP NHẬT

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 05: Đơn vị và kích thước: px, rem, %, min và max

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 02: Selector và class: chọn đúng phần tử cần sửa

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 01: Kết nối CSS với HTML và sửa quy tắc đầu tiên

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 20: Hoàn thiện website và sửa lỗi bằng DevTools

CSS cơ bản – 20 bài từ HTML đến website responsive với Flexbox, Grid và mobile-first

CSS cơ bản – Bài 03: Vì sao CSS không có tác dụng? Cascade và specificity

Giới thiệu

hocvietcode.com là website chia sẻ và cập nhật tin tức công nghệ, chia sẻ kiến thức, kỹ năng. Chúng tôi rất cảm ơn và mong muốn nhận được nhiều phản hồi để có thể phục vụ quý bạn đọc tốt hơn !

Liên hệ quảng cáo: [email protected]

Kết nối với HỌC VIẾT CODE

© hocvietcode.com - Tech888 Co .Ltd since 2019

Đăng nhập

Trở thành một phần của cộng đồng của chúng tôi!
Registration complete. Please check your email.
Đăng nhập bằng google
Đăng kýBạn quên mật khẩu?

Create an account

Welcome! Register for an account
The user name or email address is not correct.
Registration confirmation will be emailed to you.
Log in Lost your password?

Reset password

Recover your password
Password reset email has been sent.
The email could not be sent. Possible reason: your host may have disabled the mail function.
A password will be e-mailed to you.
Log in Register
×