Form và Form Validation trong SpringMVC

Chào các bạn. Có lẽ giới lập trình chúng ta đã quá quen với từ "validate" dữ liệu rồi. Chúng ta sẽ không bàn bạc về khái niệm cũng như những lợi ích của nó nữa vì nó vốn đã quá rõ ràng rồi. Bài viết ngày hôm nay của tôi sẽ hướng dẫn các bạn tạo form và validate form trong java SpringMVC nói riêng.

Tôi sẽ lấy ví dụ về việc đăng ký người dùng nhé.

1. Tạo form.

Chắc bạn đang hình dung ra việc tạo form bằng HTML với các tag form, input, textarea hay select thông thường, nhưng ý mình ở đây sẽ bao gồm 2 bước. Đó là

  • Tạo đối tượng form
  • Tạo form ở tầng view (tức là trong jsp - hay một view technology nào đó mà bạn thích sử dụng như HTML hay thymeleaf)

Nào, chúng ta bắt đầu đi tạo đối tượng form nhé.

Trước tiên, để tạo form, bạn cần đọc yêu cầu bàn toán và cân nhắc xem form của bạn sẽ bao gồm những thuộc tính gì. Ví dụ với một yêu cầu về đăng ký tài khoản thông thường, form sẽ có thể bao gòm các thuộc tính dưới đây

  • Email
  • Mật khẩu
  • Họ tên
  • Ngày sinh
  • Giới tính .v.v.v

Vậy thì chúng ta sẽ có một class RegisterForm như sau.

package forms;

import java.util.Date;

/**
 * Created by nhs3108 on 10/2/16.
 */
public class RegisterForm {
	private String email;
	private String password;
	private String passwordConfirmation;
	private String name;
	private Date birthDay;
	private String address;
	private String gender;

	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 getPasswordConfirmation() {
		return passwordConfirmation;
	}

	public void setPasswordConfirmation(String passwordConfirmation) {
		this.passwordConfirmation = passwordConfirmation;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Date getBirthDay() {
		return birthDay;
	}

	public void setBirthDay(Date birthDay) {
		this.birthDay = birthDay;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public String getGender() {
		return gender;
	}

	public void setGender(String gender) {
		this.gender = gender;
	}
}

Ok, vậy là chúng ta đã có mối class định nghĩa form cho việc đăng ký người dùng. Giờ thì chúng ta sẽ tạo form trên view. Chúng ta sẽ tạo một view, ứng với nó là một request mapping tới nhé. Ở đây tôi sẽ đặt là "/user/register".

package controllers;

import forms.RegisterForm;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

/**
 * Created by nhs3108 on 10/2/16.
 */
@Controller
public class UserController {
	@RequestMapping(value = "/user/register", method = RequestMethod.GET)
	public String register(@ModelAttribute RegisterForm registerForm) {
		return "register";
	}
}

Trong action register ta viết ở trên, ta đã khai báo rằng tầng view sẽ sử dụng một đối tượng Register đặt vào trong hash map của model. Tuơng ứng với việc sử dụng

public class UserController {
	@RequestMapping(value = "/user/register", method = RequestMethod.GET)
	public String register(Model model) {
        model.addAttribute("registerForm", new RegisterForm());
		return "register";
	}
}

Việc sử dụng ở cách bên trên trong trường hợp này sẽ gọi tới hàm contructor mặc định. Trong khi ở cách dưới, bạn có thể gọi hàm contructor bất kỳ nào. Trong file register.jsp.

<div>
        <form:form modelAttribute="registerForm" action="${pageContext.request.contextPath}/register/information"
                   cssClass="form" method="post">
            <div>
                <form:label path="email" cssclass="text-success">Email</form:label>
                <form:input path="email" type="email" placeholder="Input your email" cssClass="form-control"/>
                <form:errors path="email" cssClass="text-danger"/>
            </div>
            <div>
                <form:label path="emailConfirmation" cssclass="text-success">Email</form:label>
                <form:input path="emailConfirmation" type="email" placeholder="Confirm your email" cssClass="form-control"/>
                <form:errors path="emailConfirmation" cssClass="text-danger"/>
            </div>
            <div>
                <form:label path="password" cssclass="text-success">Email</form:label>
                <form:input path="password" type="password" placeholder="Confirm your email" cssClass="form-control"/>
                <form:errors path="password" cssClass="text-danger"/>
            </div>
            <%--Tạo input cho các thuộc tính còn lại--%>

            <button type="submit" class="btn btn-primary pull-right">SEND</button>
        </form:form>
    </div>

2. Tạo validator

OK. giờ chúng ta đã đến bước tạo validator cho nó. Trước tiên, ta tạo một Class RegisterFormValidator phục vụ cho việc validate form Register (RegisterForm). Class này phải được implement Validator của gói org.springframework.validation và như vậy, ta sẽ phải cài đặt 2 hàm supports và validate như dưới đây.

package validators;

import forms.RegisterForm;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;

/**
 * Created by nhs3108 on 10/2/16.
 */
@Component
public class RegisterFormValidator implements Validator {
	@Override
	public boolean supports(Class<?> aClass) {
        // Chỉ ra rằng nó sẽ chỉ có hiệu lực đối với RegisterForm.
		return aClass.equals(RegisterForm.class);
	}

	@Override
	public void validate(Object o, Errors errors) {
        // Thực hiện validate ở đây
	}
}

Nào, giờ thì hãy đọc requirement của bài toán đặt ra và thực hiện việc validate dữ liệu. Ta sẽ đi check tất cả các thuộc tính của form register có gửi lên server và bất cứ chỗ nào không thỏa mãn, ta sẽ add lỗi vào đối tượng Errors để kiểm tra tiếp ở phần sau (trong controller).

    @Override
	public void validate(Object o, Errors errors) {
		RegisterForm registerForm = (RegisterForm) o;
		if (!registerForm.getEmail().matches(AppConst.EMAIL_REGEX)) {
            // Khai báo thêm lỗi vào đối tượng errors
			errors.rejectValue("email", "Key message báo lỗi email sai format. (Đọc thêm về i18n)");
		}
		// Tiếp tục validate các thuộc tính khác nhé
        // Kiểm tra format, độ lớn, độ dài các kiểu
        // Kiểm tra email nhập có trùng khớp hay không
	}

Giá trị của các ràng buộc là tùy yêu cầu hệ thống của bạn. Tôi sẽ k viết thêm nhé, việc validate các thuộc tính sau là hoàn toàn tương tự. Bạn chỉ cần kiểm tra dự liệu và khi nào thấy nó k hợp lệ thì gọi hàm errors.rejectValue(String fieldName, String messageKey) là đc.

Như vậy, khi submit form register lên phía server, server sẽ nhận và validate dữ liệu, sau khi kiểm tra các dữ liệu đã đủ tiêu chuẩn thì sẽ thực hiện đăng ký người dùng và điều dướng đi đâu đó (do bạn)

    @RequestMapping(value = "/user/register", method = RequestMethod.POST)
	public String register(Model model, @ModelAttribute RegisterForm registerForm, BindingResult result, RegisterFormValidator registerFormValidator) {
        // Thực hiện validate dữ liệu
		registerFormValidator.validate(registerForm, result);
        // Kiểm tra nếu có lỗi thì yêu cầu người dùng nhập lại (hoặc return "redirect:/anywhere" để diều hướng người dùng tới 1 trang nào khác
        // Thông thừong việc validate dữ liệu (ko bao hàm nghiệp vụ nhé) đc thực hiện ở phía client. Trừ phí người dùng với mục đích xấu phá js và cố tình gửi form sai
        // Trường hợp này ta ko cần báo lỗi cụ thể, điều hướng người dùng tới 1 trang thông báo lỗi là ok
		if (result.hasErrors()) {
			return "register";
		}
		// Thực hiện các lệnh tiếp theo tùy bạn. Ví dự như kiểm tra trùng mail,v.v.v
        // Sau khi tất cả các điều kiện thoả mãn, thực thi đăng ký, điều hướng
        return "redirect:/anywhereDisplayingSuccesfully

	}

OK. Vậy là chúng ta đã thực hiện validate dữ liệu trước khi thực thi đăng ký người dùng. Hãy luôn ghi nhớ rằng "đừng bao giờ tin vào dữ liệu người dùng nhập vào", vì thế luôn validate dữ liệu trước khi lưu vào hệ thống.