0

[Java Backend Zero to Hello] BÀI 5.5: SPRING WEB MVC

Java Backend Zero to Hello

📚 Bài viết thuộc series Java Backend Zero to Hello 📌 Phần: Phase 5: Spring Framework & Spring Boot | Bài 51/86


BÀI 5.5: SPRING WEB MVC

Mục tiêu

  • Hiểu kiến trúc Spring MVC
  • Tạo Controller
  • Xử lý Request, Response
  • Sử dụng Thymeleaf template

1. KIẾN TRÚC SPRING MVC

HTTP Request
    ↓
DispatcherServlet (Front Controller)
    ↓
HandlerMapping
    ↓
Controller
    ↓
Service / Repository
    ↓
ViewResolver (cho MVC) hoặc trả JSON (cho REST)
    ↓
HTTP Response

2. @CONTROLLER

@Controller
@RequestMapping("/users")
public class UserController {

    private final UserService userService;

    public UserController(UserService userService) {
        this.userService = userService;
    }

    @GetMapping
    public String listUsers(Model model) {
        model.addAttribute("users", userService.findAll());
        return "users/list";  // Tên view (Thymeleaf template)
    }

    @GetMapping("/{id}")
    public String userDetail(@PathVariable Long id, Model model) {
        model.addAttribute("user", userService.findById(id));
        return "users/detail";
    }
}

3. @REQUESTMAPPING

@Controller
@RequestMapping("/api/users")  // Base path
public class UserController {

    @RequestMapping(method = RequestMethod.GET)  // Tương đương @GetMapping
    public String list() { ... }

    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    public String detail(@PathVariable Long id) { ... }
}

Shortcut annotations

Annotation HTTP Method
@GetMapping GET
@PostMapping POST
@PutMapping PUT
@DeleteMapping DELETE
@PatchMapping PATCH

4. NHẬN DỮ LIỆU TỪ REQUEST

4.1 @PathVariable

@GetMapping("/users/{id}")
public String getUser(@PathVariable Long id) { ... }

@GetMapping("/users/{id}/orders/{orderId}")
public String getOrder(@PathVariable Long id, @PathVariable Long orderId) { ... }

4.2 @RequestParam

@GetMapping("/search")
public String search(@RequestParam String keyword,
                     @RequestParam(defaultValue = "0") int page,
                     @RequestParam(required = false) String sort) { ... }

// URL: /search?keyword=java&page=1&sort=name

4.3 @RequestBody

@PostMapping("/users")
public String create(@RequestBody UserDTO user) { ... }

4.4 @RequestHeader

@GetMapping("/users")
public String list(@RequestHeader("Authorization") String token) { ... }

4.5 @CookieValue

@GetMapping("/profile")
public String profile(@CookieValue("sessionId") String sessionId) { ... }

4.6 @ModelAttribute

@PostMapping("/users")
public String create(@ModelAttribute UserForm form) { ... }

5. TRẢ VỀ DỮ LIỆU

5.1 String (View name)

@GetMapping("/users")
public String list(Model model) {
    model.addAttribute("users", userService.findAll());
    return "users/list";  // users/list.html
}

5.2 ModelAndView

@GetMapping("/users")
public ModelAndView list() {
    ModelAndView mav = new ModelAndView("users/list");
    mav.addObject("users", userService.findAll());
    return mav;
}

5.3 ResponseEntity (REST)

@GetMapping("/api/users/{id}")
public ResponseEntity<User> getUser(@PathVariable Long id) {
    return userService.findById(id)
        .map(ResponseEntity::ok)
        .orElse(ResponseEntity.notFound().build());
}

6. MODEL

@GetMapping("/users")
public String list(Model model) {
    model.addAttribute("users", userService.findAll());
    model.addAttribute("title", "Danh sách user");
    return "users/list";
}

Thymeleaf template

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title th:text="${title}">Title</title>
</head>
<body>
    <h1 th:text="${title}">Title</h1>
    <table>
        <tr th:each="user : ${users}">
            <td th:text="${user.id}">1</td>
            <td th:text="${user.name}">Name</td>
            <td th:text="${user.email}">Email</td>
        </tr>
    </table>
</body>
</html>

7. FORM HANDLING

7.1 Controller

@GetMapping("/users/new")
public String showForm(Model model) {
    model.addAttribute("user", new UserForm());
    return "users/form";
}

@PostMapping("/users")
public String create(@ModelAttribute("user") @Valid UserForm form,
                     BindingResult result) {
    if (result.hasErrors()) {
        return "users/form";
    }
    userService.save(form);
    return "redirect:/users";
}

7.2 Form template

<form th:action="@{/users}" th:object="${user}" method="post">
    <div>
        <label>Name:</label>
        <input type="text" th:field="*{name}" />
        <span th:errors="*{name}"></span>
    </div>
    <div>
        <label>Email:</label>
        <input type="email" th:field="*{email}" />
        <span th:errors="*{email}"></span>
    </div>
    <button type="submit">Save</button>
</form>

8. REDIRECT VÀ FORWARD

@PostMapping("/users")
public String create(@ModelAttribute UserForm form) {
    userService.save(form);
    return "redirect:/users";  // Redirect (PRG pattern)
}

@GetMapping("/old-path")
public String oldPath() {
    return "forward:/new-path";  // Forward
}

9. SESSION VÀ FLASH ATTRIBUTES

9.1 @SessionAttributes

@Controller
@SessionAttributes("user")
public class UserController {

    @ModelAttribute("user")
    public UserForm getUser() {
        return new UserForm();
    }
}

9.2 RedirectAttributes (Flash)

@PostMapping("/users")
public String create(@ModelAttribute UserForm form, RedirectAttributes ra) {
    userService.save(form);
    ra.addFlashAttribute("message", "Tạo user thành công!");
    return "redirect:/users";
}

10. EXCEPTION HANDLING

10.1 @ExceptionHandler

@ExceptionHandler(UserNotFoundException.class)
public String handleNotFound(UserNotFoundException ex, Model model) {
    model.addAttribute("error", ex.getMessage());
    return "error/404";
}

10.2 @ControllerAdvice

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(UserNotFoundException.class)
    public String handleNotFound(UserNotFoundException ex, Model model) {
        model.addAttribute("error", ex.getMessage());
        return "error/404";
    }

    @ExceptionHandler(Exception.class)
    public String handleGeneric(Exception ex, Model model) {
        model.addAttribute("error", "Đã xảy ra lỗi");
        return "error/500";
    }
}

11. INTERCEPTOR

public class AuthInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        // Kiểm tra trước khi vào controller
        if (!isAuthenticated(request)) {
            response.sendRedirect("/login");
            return false;
        }
        return true;
    }
}

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AuthInterceptor())
                .addPathPatterns("/admin/**");
    }
}

12. STATIC RESOURCES

spring:
  web:
    resources:
      static-locations: classpath:/static/, classpath:/public/

Mặc định: /static/, /public/, /resources/, /META-INF/resources/


13. BÀI TẬP THỰC HÀNH

Bài 1: CRUD với Thymeleaf

Tạo ứng dụng quản lý user với:

  • Danh sách user
  • Form thêm/sửa
  • Xóa user

Bài 2: Validation

Thêm validation cho form (name không rỗng, email hợp lệ).

Bài 3: Exception Handling

Xử lý UserNotFoundException với trang 404 đẹp.


14. TÓM TẮT

Khái niệm Mô tả
@Controller MVC Controller
@RequestMapping Map URL
@PathVariable Biến trong URL
@RequestParam Query parameter
@RequestBody JSON body
@ModelAttribute Form binding
Model Truyền data sang view
Thymeleaf Template engine
@ControllerAdvice Global exception handler
Interceptor Xử lý trước/sau request

Bài tiếp theo: 5.6 Spring Boot REST


🧭 Điều Hướng Series

⬅️ Bài trước: BÀI 5.4: SPRING DATA JPA

📋 Lộ trình tổng quan: Xem Toàn Bộ Series

➡️ Bài tiếp theo: BÀI 5.6: SPRING BOOT REST API


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí