Giới thiệu
Validation là thao tác cho
phép ứng dụng kiểm tra dữ liệu nhập vào bởi người dùng để đảm bảo tính hợp lý
và chính xác khi xử lý các thao tác nghiệp vụ. Chẳng hạn, khi cần nhập địa chỉ
email, ứng dụng cần đảm bảo email đó là hợp lệ.
- Validation có thể được thực
hiện ở các tầng khác nhau, chẳng hạn:
- Validate ở tầng giao diện:
Sử dụng JavaScript để validate
- Validate ở tầng back-end:
Validate ở Controller/Service
- Validate ở tầng CSDL: Sử
dụng các ràng buộc trong CSDL (NOT NULL, UNIQUE...)
Spring hỗ trợ một số phương pháp khác nhau để validate ở
tầng back-end, chẳng hạn như sử dụng annotation hoặc tự tạo các đối tượng
validator.
Validation sử dụng interface Validator của Spring
Spring hỗ trợ interface Validator để thực hiện các thao
tác validate. Interface Validator hoạt động bằng cách sử dụng một đối tượng
Errors trong khi tiến hành xác minh, đối tượng xác minh có thể báo cáo cho đối
tượng Error những sai phạm trong quá trình xác minh.
Hãy xem xét ví dụ nhỏ sau đây:
public class Person
{
private String
name;
private int age;
// the usual getters and setters...
}
Chúng ta sẽ validate dữ liệu của Person bằng cách triển
khai hai phương thức sau của
interface org.springframework.validation.Validator:
- support(Class) - Validator này được dùng cho các đối tượng
thuộc lớp Class?
- validate(Object, org.springframework.validation.Errors) -
xác minh đối tượng được truyền vào, trong trường hợp xác minh thấy lỗi, chúng sẽ
được đưa vào trong đối tượng Errors.
Triển khai một Validator là khá đơn giản, đặc biệt khi bạn
biết lớp trợ giúp ValidationUtil cũng được Spring cung cấp sẵn.
public class PersonValidator
implements Validator {
/**
* This Validator validates *just*
Person instances
*/
public boolean supports(Class
clazz) {
return Person.class.equals(clazz);
}
public void validate(Object obj, Errors e) {
ValidationUtils.rejectIfEmpty(e, "name", "name.empty");
Person p = (Person) obj;
if (p.getAge() < 0) {
e.rejectValue("age", "negativevalue");
} else
if (p.getAge() > 110) {
e.rejectValue("age", "too.darn.old");
}
}
}
Như bạn thấy, phương thức static rejectIfEmpty(..)
của lớp ValidationUtil được sử dụng để từ chối thuộc tính ‘name’ nếu nó
là null hoặc chuỗi trống. Hãy đọc thêm tài liệu mô tả lớp ValidationUtils để
thấy những gì mà chức năng này cung cấp bên cạnh những thứ thấy được trong ví dụ
bên trên.
Mặc dù chắc chắn có thể triển khai một lớp Validator dể
xác minh cho mỗi đối tượng lồng trong một đối tượng ‘lớn’, tốt hơn là nên
bao gói lô-gic xác minh vào trong mỗi lớp lồng nhau của đối tượng bên trong triển
khai Validator của nó. Một ví dụ đơn giản về một đối tượng ‘lớn’ là
một đối tượng Customer, nó có hai thuộc tính String (firstName và surname) và một
đối tượng Address phức tạp. Các đối tượng Address có thể được sử dụng độc lập với
các đối tượng Customer, và vì vậy cũng cần triển khai một lớp AddressValidator.
Nếu bạn muốn lớp CustomerValidator sử dụng lại các lô gic của lớp
AddressValidator mà không cần phải copy-and-paste, bạn có thể sử dụng
Dependency Injection hoặc khởi tạo một đối tượng AddressValidator bên trong lớp
CustomerValidator, và sử dụng nó giống như sau:
public class CustomerValidator
implements Validator {
private final Validator
addressValidator;
public CustomerValidator(Validator addressValidator) {
if (addressValidator
== null) {
throw new IllegalArgumentException("The supplied [Validator] is
" +
"required and must not be
null.");
}
if (!addressValidator.supports(Address.class)) {
throw new IllegalArgumentException("The supplied [Validator] must
" +
"support the validation of
[Address] instances.");
}
this.addressValidator = addressValidator;
}
/**
* This Validator validates Customer
instances, and any subclasses of Customer too
*/
public boolean supports(Class
clazz) {
return Customer.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", "field.required");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "surname", "field.required");
Customer customer = (Customer) target;
try {
errors.pushNestedPath("address");
ValidationUtils.invokeValidator(this.addressValidator, customer.getAddress(), errors);
} finally
{
errors.popNestedPath();
}
}
}
Các lỗi về xác minh được báo cho đối tượng Errors. Trong
trường hợp Spring Web MVC bạn có thể sử dụng thẻ <spring:bind/> để quan
sát các thông báo lỗi, nhưng tất nhiên bạn cũng có thể quan sát đối tượng lỗi của
chính mình.
Xử lý mã đối với các thông báo lỗi
Trong ví dụ trước, chúng ta đã từ chối trường name và
age. Nếu chúng ta đưa ra thông báo lỗi bằng cách sử dụng MessageSource, chúng
ta sẽ sử dụng mã lỗi được chúng ta đưa ra khi từ chối thuộc tính (trong trường
hợp này là ‘name’ và ‘age’). Khi bạn gọi (hoặc là trực tiếp, hoặc gián tiếp, sử
dụng ví dụ lớp ValidationUtils) rejectValue hoặc một trong những phương thức
reject từ interface Error, triển khai cơ bản sẽ không chỉ đăng ký mã bạn đã
truyền vào, mà còn một số mã lỗi bổ sung. Các mã lỗi gì được sử dụng mà nó đăng
ký được xác định bởi MessageCodesResolver. Mặc định, DefaultMessageCodesResolver được
sử dụng, ví dụ, không chỉ đăng ký một thông điệp với lỗi mà bạn đã cung cấp, mà
còn các thông điệp chứa thuộc tính name mà bạn truyền vào phương thức reject.
Do đó trong trường hợp bạn từ chối một thuộc tính sử dụng phương thức rejectValue(“age”,
“too.darn.old”), ngoài mã too.darn.old, Spring cũng sẽ đăng ký too.darn.old.age
và too.darn.old.age.int (cái thứ nhất chứa thuộc tính name và cái thứ hai chứa
kiểu của thuộc tính); điều này được thực hiện như một sự tiện lợi để hỗ trợ các
nhà phát triển nhắm đến việc đưa ra các thông báo lỗi và những điều tương tự.
Validation trong Spring
Tổng quan về JSR-303 Bean Validation API
JSR-303 tiêu chuẩn hóa việc khai báo ràng buộc validation
và metadata cho nền tảng Java. Sử dụng API này, bạn sử dụng annotation kết hợp
với các ràng buộc validation cho các thuộc tính của model và runtime sẽ thực
thi chúng. Có một số ràng buộc tích hợp sẵn mà bạn có thể sử dụng. Bạn cũng có
thể định nghĩa các ràng buộc của mình.
Để minh họa cho điều này, hãy xem xét ví dụ về model
PersonForm với hai thuộc tính sau:
public class PersonForm
{
private String
name;
private int age;
}
JSR-303 cho phép bạn định nghĩa cách khai báo các ràng buộc
validation cùng với các thuộc tính đó:
public class PersonForm
{
@NotNull
@Size(max=64)
private String
name;
@Min(0)
private int age;
}
Khi một đối tượng của lớp này được xác minh bởi Validator
chuẩn JSR-303, các ràng buộc đó sẽ được thực thi.
Xem trang web Bean Validation để biết
thông tin chung về JSR-303/JSR-349. Thông tin về các khả năng cụ thể của triển
khai mặc định xem trong các tài liệu Hibernate Validator. Để học cách cài đặt một bộ cung cấp Bean
Validation giống như một bean trong Spring hãy đọc phần tiếp theo.
Cấu hình một bộ cung cấp Bean Validation
Spring cung cấp đầy đủ các hỗ trợ cho Bean Validation
API. Bao gồm hỗ trợ thuận tiện cho việc khởi động một bộ cung cấp
JSR-303/JSR-349 Bean Validation như là một bean trong Spring. Điều đó cho phép
javax.validation.ValidatorFactory hoặc javax.validation.Validator được tiêm vào
bất cứ đâu mà ứng dụng cần.
Sử dụng the LocalValidatorFactoryBean để
cấu hình một Validator mặc định như một Spring bean:
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
Cấu hình căn bản ở trên sẽ kích hoạt Bean Validation khởi
tạo sử dụng cơ chế khởi động mặc định của nó. Một bộ cung cấp JSR-303/JSR-349,
như là Hibernate Validator, được mong chờ để được thể hiện trong classpath và sẽ
được tự động xác định.
Tiêm một Validator
LocalValidatorFactoryBean triển
khai cả hai
interface javax.validation.ValidatorFactory và javax.validation.Validator,
cũng như org.springframework.validation.Validator trong Spring. Bạn
có thể tiêm một tham chiếu đến một trong hai giao diện này vào các bean cần triệu
gọi lô-gic validation.
Tiêm một tham chiếu tới javax.validation.Validator nếu
bạn muốn làm việc trực tiếp với Bean Validation API:
import javax.validation.Validator;
@Service
public class MyService {
@Autowired
private Validator
validator;
Tiêm một tham chiếu tới org.springframework.validation.Validator
nếu bean của bạn yêu cầu Spring Validation API:
import org.springframework.validation.Validator;
@Service
public class MyService
{
@Autowired
private Validator
validator;
Cấu hình các ràng buộc tùy chỉnh
Mỗi ràng buộc Bean Validation gồm hai phần. Thứ nhất, một
annotation @Constraint để khai báo ràng buộc và các thuộc tính có thể cấu hình
của nó. Thứ hai, Một triển khai của giao diện javax.validation.ConstraintValidator để
triển khai các hành vi của ràng buộc. Để liên kết một khai báo với một triển
khai, mỗi annotation @Constraint tham chiếu tới một lớp triển khai
ValidationConstraint tương ứng. Tại runtime, một ConstraintValidatorFactory khởi
tạo đối tượng của triển khai được tham chiếu khi bắt gặp một annotation về ràng
buộc trong model.
Mặc định, LocalValidatorFactoryBean cấu hình một SpringConstraintValidatorFactory,
cái sử dụng Spring để tạo các đối tượng ConstraintValidator. Điều này cho phép
các ConstraintValidator tùy chỉnh hưởng lợi từ tiêm phụ thuộc giống như bất kỳ
một bean nào trong Spring.
Hãy xem ví dụ dưới đây về một khai báo @Constraint tùy chỉnh,
tiếp theo là một triển khai của ConstraintValidator được liên kết, cái sử dụng
Spring cho tiêm phụ thuộc:
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy=MyConstraintValidator.class)
public @interface MyConstraint
{
}
import javax.validation.ConstraintValidator;
public class MyConstraintValidator
implements ConstraintValidator {
@Autowired;
private Foo aDependency;
...
}
Như bạn có thể thấy, một triển khai ConstraintValidator
có thể có các @Autowired phụ thuộc của nó giống như bất kỳ một bean nào đó
trong Spring.
Validation phương thức hướng-Spring
Tính năng validation phương thức được hỗ trợ bởi Bean
Validation 1.1, và như một mở rộng tùy chỉnh hỗ trợ bởi Hibernate Validator
4.3, có thể được tích hợp trong một Spring context thông qua một định nghĩa
bean MethodValidationPostProcessor:
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
Để đủ điều kiện cho validation phương thức hướng-Spring,
tất cả các lớp mục tiêu cần được chú thích bằng @Validatedannotation, khai
báo các nhóm validation tùy chọn để sử dụng. Xem tài liệu về MethodValidationPostProcessor để
cài đặt chi tiết với Hibernate Validator và bộ cung cấp Bean Validation 1.1.
Các lựa chọn cấu hình bổ sung
Cấu hình LocalValidatorFactoryBean mặc định nên
chứng tỏ là đủ cho hầu hết các trường hợp. Có một số lựa chọn cấu hình cho các
cấu trúc Bean Validation khác nhau, từ nội suy thông điệp đến traversal
resolution. Xem tài liệu LocalValidatorFactoryBean để có thêm thông
tin về các lựa chọn này.
Cấu hình một DataBinder
Kể từ Spring 3.0, một đối tượng DataBinder có thể được cấu
hình với một Validator. Sau khi cấu hình, Validator có thể được triệu gọi bằng
cách gọi phương thức binder.validate(). Bất kỳ một đối tượng Error về
validation nào đều được tự động thêm vào BindingResult của binder.
Khi làm việc với DataBinder, điều này có thể được sử dụng
để triệu gọi lô-gic validation sau khi binding tới một đối tượng:
Foo target =
new Foo();
DataBinder binder
= new DataBinder(target);
binder.setValidator(new FooValidator());
// bind to the target object
binder.bind(propertyValues);
// validate the target object
binder.validate();
// get BindingResult that
includes any validation errors
BindingResult results = binder.getBindingResult();
Một DataBinder cũng có thể được cấu hình với nhiều đối tượng
Validator bằng phương thức dataBinder.addValidatorsvà dataBinder.replaceValidators.
Điều này hữu ích khi kết hợp Bean Validation được cấu hình toàn cục với một
Spring Validatorconfigured cục bộ trên một đối tượng DataBinder. Đọc thêm
tại [validation-mvc-configuring].
Validation trong Spring MVC
3
Tham khảo Validation trong nội
dung về Spring MVC
CodeLean.vn
Đăng nhận xét