Bài đăng nổi bật

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ự.
Tìm hiểu thêm về MessageCodesResolver và các chiến lược mặc định trong các tài liệu trực tuyến về Java như: MessageCodesResolver và DefaultMessageCodesResolver, v.v...

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

Post a Comment

أحدث أقدم