Triển khai mô hình RESTful Web Service bằng Spring MVC

1. Cấu trúc và Tổ chức Dự án

Dự án được xây dựng dựa trên kiến trúc Maven Web tiêu chuẩn, tập trung vào việc phân chia rõ ràng giữa các tầng xử lý dữ liệu và giao diện người dùng. Sơ đồ tổ chức thư mục như sau:

src/main/
├── java/
│   └── vn.tech.app/
│       ├── model/          # Các thực thể dữ liệu (Account, Location)
│       ├── controller/     # Điều hướng và xử lý logic yêu cầu
│       └── repository/     # Tầng truy xuất dữ liệu (Mock DAO)
├── resources/
│   └── spring-config.xml   # Cấu hình Spring Context và View Resolver
└── webapp/
    ├── WEB-INF/
    │   ├── views/          # Thư mục chứa các tệp JSP
    │   └── web.xml         # Cấu hình Web Deployment Descriptor
    └── static/             # Tài nguyên tĩnh

2. Cấu hình Hệ thống

Maven (pom.xml)

Để bắt đầu với Spring MVC, chúng ta cần khai báo các phụ thuộc cốt lõi bao gồm spring-webmvcspring-web. Đảm bảo phiên bản tương thích để tránh xung đột thư viện.

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.3.RELEASE</version>
</dependency>

Web Deployment Descriptor (web.xml)

Cấu hình quan trọng nhất ở đây là DispatcherServlet và các Filter xử lý mã hóa dữ liệu cũng như hỗ trợ các phương thức HTTP đặc biệt (PUT/DELETE).

  • CharacterEncodingFilter: Đảm bảo dữ liệu gửi lên không bị lỗi font (UTF-8).
  • HiddenHttpMethodFilter: Chuyển đổi các request POST có tham số _method thành các request PUT hoặc DELETE tương ứng để tuân thủ chuẩn REST.

3. Xây dựng Tầng Model và Repository

Thay vì sử dụng các đối tượng cũ, chúng ta định nghĩa các lớp POJO mới để đại diện cho dữ liệu người dùng.

// Thực thể thông tin địa chỉ
public class LocationInfo {
    private String province;
    private String city;
    // Getters and Setters
}

// Thực thể thành viên hệ thống
public class Member {
    private Long uid;
    private String username;
    private Integer age;
    private LocationInfo location;
    // Getters and Setters
}

Tầng Repository mô phỏng việc tương tác với cơ sở dữ liệu bằng cách sử dụng các dòng log để kiểm tra luồng hoạt động.

@Repository
public class MemberRepository {
    public void createMember(Member member) {
        System.out.println("Thêm mới thành viên: " + member.getUsername());
    }

    public void removeById(Long id) {
        System.out.println("Xóa thành viên ID: " + id);
    }

    public void refreshInfo(Long id) {
        System.out.println("Cập nhật thành viên ID: " + id);
    }
}

4. Xử lý Request và Thiết kế RESTful API

Phân biệt @RequestParam và @PathVariable

Trong Spring MVC, việc tiếp nhận dữ liệu từ URL có hai cách phổ biến:

  • @RequestParam: Dùng cho Query String (ví dụ: /search?name=abc).
  • @PathVariable: Dùng cho cấu trúc URL phân cấp (ví dụ: /member/101).
@Controller
public class ParamController {

    @RequestMapping("/fetch")
    public String getParam(@RequestParam(value = "uid", defaultValue = "0") String userId) {
        System.out.println("User ID nhận được: " + userId);
        return "view_success";
    }

    @RequestMapping("/header-info")
    public String checkHeader(@RequestHeader("User-Agent") String agent) {
        System.out.println("Trình duyệt đang dùng: " + agent);
        return "view_success";
    }
}

Triển khai CRUD theo chuẩn REST

Sử dụng cùng một endpoint nhưng khác phương thức HTTP để thực hiện các hành vi khác nhau.

@Controller
@RequestMapping("/api/v1/members")
public class RestMemberController {

    @Autowired
    private MemberRepository repo;

    // GET: Truy vấn dữ liệu
    @RequestMapping(method = RequestMethod.GET)
    public String listAll() {
        System.out.println("Lấy danh sách thành viên");
        return "view_success";
    }

    // POST: Tạo mới
    @RequestMapping(method = RequestMethod.POST)
    public String add() {
        repo.createMember(new Member());
        return "view_success";
    }

    // PUT: Cập nhật
    @RequestMapping(method = RequestMethod.PUT)
    public String update(Long id) {
        repo.refreshInfo(id);
        return "view_success";
    }

    // DELETE: Xóa bỏ
    @RequestMapping(method = RequestMethod.DELETE)
    public String delete(Long id) {
        repo.removeById(id);
        return "view_success";
    }
}

5. Giao diện và Liên kết Dữ liệu (Data Binding)

Để Spring MVC có thể tự động ánh xạ dữ liệu từ Form vào đối tượng Java (POJO), tên của các trường input trong HTML phải khớp với tên thuộc tính của lớp.

<!-- Form cập nhật thông tin thành viên -->
<form action="/test-bind" method="post">
    ID: <input type="text" name="uid"> <br>
    Tên: <input type="text" name="username"> <br>
    Tỉnh thành: <input type="text" name="location.province"> <br>
    <input type="submit" value="Gửi dữ liệu">
</form>

Khi sử dụng các phương thức không phải GET/POST trong form HTML truyền thống, chúng ta tận dụng HiddenHttpMethodFilter bằng cách thêm một trường ẩn:

<form action="/api/v1/members" method="post">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="Xóa tài khoản">
</form>

6. Làm việc với Servlet API gốc

Trong một số trường hợp, lập trình viên cần can thiệp trực tiếp vào HttpServletRequest hoặc HttpSession. Spring MVC hỗ trợ truyền trực tiếp các đối tượng này vào tham số của phương thức trong Controller.

@RequestMapping("/native-api")
public String handleNative(HttpServletRequest request, HttpSession session) {
    request.setAttribute("msg", "Dữ liệu từ Request Scope");
    session.setAttribute("user_session", "Dữ liệu phiên làm việc");
    return "view_success";
}

Thẻ: Spring MVC RESTful API Java Web Maven Web Development

Đăng vào ngày 27 tháng 7 lúc 17:50