Cơ chế truyền và ràng buộc dữ liệu trong Spring MVC

Ràng buộc URL với phương thức xử lý

Annotation @RequestMapping(value = "/test/info", method = RequestMethod.GET) dùng để ánh xạ một phương thức trong Controller tới một URL cụ thể. Mặc định, nó chấp nhận cả yêu cầu GET và POST, nhưng có thể giới hạn bằng thuộc tính method. Tuy nhiên, các annotation chuyên biệt hơn như @GetMapping@PostMapping thường được ưa chuộng hơn cho từng phương thức. Trong khi đó, @RequestMapping thường được dùng ở cấp lớp để định nghĩa tiền tố chung cho tất cả các endpoint bên trong.

Nhận tham số từ yêu cầu HTTP

Truyền tham số trực tiếp theo tên

Khi tên trường trong form HTML trùng với tên tham số trong phương thức Controller, Spring MVC sẽ tự động gán giá trị:

<form action="/user/submit" method="post">
  <input name="account">
  <input name="secret">
  <input type="submit" value="Gửi">
</form>
@Controller
public class UserController {
    @PostMapping("/submit")
    @ResponseBody
    public String handle(String account, String secret) {
        // Xử lý logic
        return "OK";
    }
}

Spring MVC hỗ trợ chuyển đổi kiểu dữ liệu tự động (ví dụ: chuỗi số → Long). Tuy nhiên, nếu dữ liệu không hợp lệ (ví dụ: "xyz" → Long), hệ thống sẽ trả về lỗi 400 do không thể chuyển đổi.

Sử dụng @RequestParam

Khi tên tham số trên client không phù hợp với quy tắc đặt tên biến Java (ví dụ: user_name), hoặc khi cần xử lý dữ liệu phức tạp như mảng, danh sách, ta sử dụng @RequestParam:

@PostMapping("/apply")
@ResponseBody
public String processForm(
    @RequestParam(value = "user_name", defaultValue = "Ẩn danh") String userName,
    String course,
    @RequestParam List<Integer> purpose
) {
    System.out.println("Tên: " + userName);
    System.out.println("Khóa học: " + course);
    purpose.forEach(System.out::println);
    return "Thành công";
}

Lưu ý: Khi dùng Map<String, String> để nhận toàn bộ tham số, các giá trị lặp (như checkbox nhiều lựa chọn) sẽ bị ghi đè — chỉ giữ lại giá trị đầu tiên. Do đó, nên dùng đối tượng DTO (Data Transfer Object) để đảm bảo toàn vẹn dữ liệu.

Xử lý JSON với @RequestBody

Khi frontend gửi dữ liệu dưới dạng JSON (thường gặp khi dùng Axios, Fetch API...), header Content-Type sẽ là application/json. Trong trường hợp này, Controller phải dùng @RequestBody để Spring MVC biết cần phân tích nội dung body thành đối tượng Java:

@PostMapping("/api/user")
@ResponseBody
public String createUser(@RequestBody User user) {
    // Xử lý đối tượng user đã được deserialize từ JSON
    return "Tạo thành công";
}

Ngược lại, với form HTML truyền thống (mặc định application/x-www-form-urlencoded), không cần @RequestBody khi dùng đối tượng DTO.

Gán giá trị cho thuộc tính đối tượng lồng nhau

Spring MVC hỗ trợ gán dữ liệu vào các đối tượng con thông qua cú pháp dấu chấm trong tên input:

<input name="credential.username">
<input name="credential.password">
<input name="profile.fullName">
<input name="profile.birthDate">
public class RegistrationForm {
    private Credential credential = new Credential();
    private Profile profile = new Profile();
}

public class Credential {
    private String username;
    private String password;
}

public class Profile {
    private String fullName;
    private Date birthDate;
}

Dữ liệu sẽ được ánh xạ chính xác vào các thuộc tính lồng nhau nhờ cơ chế data binding của Spring.

Trích xuất biến từ đường dẫn URL với @PathVariable

Annotation này cho phép lấy giá trị từ các đoạn động trong URL:

@GetMapping("/orders/{orderId}/items/{itemId}")
@ResponseBody
public String getItemDetail(
    @PathVariable Long orderId,
    @PathVariable("itemId") String productId
) {
    return "Đơn hàng: " + orderId + ", Sản phẩm: " + productId;
}

Xử lý tham số kiểu ngày tháng

Chuyển đổi cục bộ với @DateTimeFormat

Áp dụng trực tiếp trên tham số hoặc thuộc tính đối tượng để chỉ định định dạng ngày:

@PostMapping("/events")
@ResponseBody
public String createEvent(
    @DateTimeFormat(pattern = "yyyy-MM-dd") Date eventDate,
    @RequestBody Event event
) {
    // ...
}

Hoặc trong lớp DTO:

public class Event {
    @DateTimeFormat(pattern = "dd/MM/yyyy")
    private LocalDate startDate;
}
Cấu hình chuyển đổi toàn cục

Tạo converter tùy chỉnh:

public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        if (source == null || source.trim().isEmpty()) return null;
        try {
            return new SimpleDateFormat("yyyy-MM-dd").parse(source);
        } catch (ParseException e) {
            throw new IllegalArgumentException("Không thể phân tích ngày: " + source);
        }
    }
}

Đăng ký trong file cấu hình Spring (XML):

<mvc:annotation-driven conversion-service="globalConversionService" />

<bean id="globalConversionService"
      class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
    <property name="converters">
        <set>
            <bean class="com.example.converter.StringToDateConverter" />
        </set>
    </property>
</bean>

Sau khi cấu hình, mọi tham số kiểu Date nhận chuỗi có định dạng yyyy-MM-dd sẽ được tự động chuyển đổi mà không cần annotation.

Thẻ: SpringMVC Java Web @RequestParam @RequestBody @PathVariable

Đăng vào ngày 4 tháng 7 lúc 05:59