Design pattern for cumulative violations

Imagine that we have a process that accepts data of the following type:

{"date":"2014-05-05", "url":"http://some.website.com","counter":3}
  • This data must be confirmed formally : the value datemust be parseable date, urlmust also comply with the standard syntax url.
  • In addition, this data must be logically confirmed : it datemust be in the future url, the returning address must be available 200 OK.

To make it clean, you need to separate these two verification procedures into different units (classes, utils, whatever). However, the desired final behavior should give the user a clear idea of ​​ALL violations that are present in the data. Sort of:

{"Errors":[ "Specified date is not in the future",//Formal validation failed "Specified URL has invalid syntax"//Logical validation failed ]}

  • I have seen some implementations of the required behavior, but they use those that use objects Error, and are full of checks like Error.hasErrors()or error==null, which does not look elegant.
  • I also saw an implementation javax.validationthat gives you all the violations in the entire field at once. The same approach can be implemented to check the content, but I'm not sure if this is the best way to do this.

Question : What is the best practice for handling multiple exceptions / violations of various nature?

UPD: short digest of answers: collect Violations, build Exception, containing their context, reason and description, use an interceptor to render. See links to links from the answers:

http://beanvalidation.org/1.0/spec/ JSR 303 Specification

http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html Spring Bean

http://docs.oracle.com/javaee/6/tutorial/doc/gircz.html Java EE

?

+4
5

:

:

public abstract class Check {
  private final List<Check> subChecks = new ArrayList<Check>();

  public Check add(Check subCheck) { subChecks.add(subCheck); return this }

  public void run(Data dataToInspect, List<Error> errors) {
    Error e = check(dataToInspect);
    if (e != null) {
       errors.add(e);
       return;
    }
    for (Check subCheck : subChecks) {
      subCheck.run(dataToInspect, errors);
    }
  }

  // Returns null if dataToInspect is OK.
  public abstract Error check(Data dataToInspect);
}

class Data - , ( ). String, JSON, .

class Error , , :

public class Error {
  private String problem;
  public Error(String problem) { this.problem = problem }
  public String getProblem() { return problem }
  // maybe additional fields and method to better describe the detected problem...
}

, :

public class Checker {

   private final List<Error> errors = new ArrayList<Error>();
   private final List<Check> checks = new ArrayList<Check>();

   public Checker() {
     checks.add(new DateIsParsableCheck().add(new DateIsInTheFurutreCheck());
     checks.add(new UrlIsWellFormed().add(new UrlIsAccessible());

     checks.add();
     ..
   }

   public void check(Data d) {
     for (Check c : checks) {
       Error e = c.run(d, errors);
       if (e != null) 
         errors.add(e);
     }
   }
}

. : x y, y , x . , Date , .

, / .

+3

, , , . -, . , . , , , . ( , , ). getter, () . , i18n.

- @Alexandre Santos. , . . , /:

public static void main(String[] args) {
    Violations violations = new Violations();
    Integer age = AgeValidator.parse("0042", "age", violations);
    URL url = UrlValidator.parse("http://some.website.com", "url", violations);
}

// Validator defining all the rules for a valid age value
public class AgeValidator {

    // Collection of validation rules for age values
    private static final Collection<Validator<String>> VALIDATORS = ...;

    // Pass in the value to validate, the name of the field
    // defining the value and the container to collect all
    // violations (could be a Map<String, ValidationException>)
    //
    // a return value of null indicates at least one rule violation
    public static Integer parse(String value, String name, Violations violations) {
        try {
            for (Validator<String> validator : VALIDATORS) validator.validate(value);
        } catch (ValidationException e) {
            violations.add(name, e);
        }
        return violations.existFor(name) ? null : Integer.parseInt(value);
    }
}
+3

, , . , . , . , ... .

( , ) , . JSR 303 Bean Spring: http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html

, , 1000 .

+1

, , , ()

, . - , , , - + , .

@Aspect
public class DtoValidator {

private Validator validator;

public DtoValidator() {
}

public DtoValidator(Validator validator) {
    this.validator = validator;
}

public void doValidation(JoinPoint jp){
    for( Object arg : jp.getArgs() ){
        if (arg != null) {
            Set<ConstraintViolation<Object>> violations = validator.validate(arg);
            if( violations.size() > 0 ){
                throw buildError(violations);
            }
        }
    }
}

private static BadRequestException buildError( Set<ConstraintViolation<Object>> violations ){
    Map<String, String> errorMap = new HashMap<String, String>();
    for( ConstraintViolation error : violations ){
        errorMap.put(error.getPropertyPath().toString(), error.getMessage());
    }
    return new BadRequestException(errorMap);
}
}

bean config

<aop:config proxy-target-class="true">
    <aop:aspect id="dtoValidator" ref="dtoValidator" order="10">
        <aop:before method="doValidation"
                    pointcut="execution(public * com.mycompany.ws.controllers.bs.*.*(..))"/>
    </aop:aspect>
</aop:config>

, .

+1

. , , , , , , (, ) ID ... .

0

Source: https://habr.com/ru/post/1540333/


All Articles