Question

Let's imagine, that we have a process, which accepts data of the following type:

{"date":"2014-05-05", "url":"http://some.website.com","counter":3}
  • This data should be validated formally: value of date should be a parseable date, url should also conform the normal url syntax.
  • Also, this data should be validated logically: date should be in the future, url should be an accessible address, returning 200 OK.

To make it clean, one must separate those two validation routines into different units (classes, utils, whatever). The desired final behaviour, however, must give user clear understanding of ALL violations, that are present in data. Something like:

{"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 behaviour, but they use those make use of Error objects and are full of checks like Error.hasErrors() or error==null, which does not look elegant.
  • I have also seen the implementation of javax.validation, which gives you all violations on all field at once. Same approach could be implemented for content validation, but I am not sure, that 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 an Exception, containing their context, cause and description, use an interceptor to render. See reference links from 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 Validation

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

Which Design Pattern To Use For Validation

Why not use exceptions as regular flow of control?

Était-ce utile?

La solution

You can do the following:

define an abstract Check class, as follows:

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 is the class holding the data (that needs to be checked). Can be a String, a JSON object, what have you.

class Error represents a problem detected in the data should be roughly something like:

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...
}

You then have code that runs the check against piece of data:

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);
     }
   }
}

Slightly changed my original answer. In the current answer there is the notion of subchecks: if a check called x has a subcheck called y then the y check will run only if the x check succeeded. For instance, if the Date is not parseable there is no point to check it it is in the future.

In your case I think that all/most logical check should be sub-checks of a formal check.

Autres conseils

I don't think there is a best practice, because it depends on what you try to achieve. In my opinion, exceptions and their messages should not be used to be displayed directly to the user. Exceptions are way too technical and do depend heavily on the context where they get thrown.

Hence, my approach would be to design a container type which collects all the exceptions thrown by your validations. Those exceptions should preserve as much of the context as possible (not in form of an exception message, but in form of fields passed into the constructor). Provide getter methods to make those fields (properties) accessible. When rendering the view, you may iterate over all entries of your container and generate a proper, human readable, i18n message.

Here is some pseudo-code as requested by the comment of @AlexandreSantos. It is not polished nor proven, just my first draft. So do not expect excellent design. It's just an example how it could be implemented / designed:

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);
    }
}

I have answered this previously Here

The answer marked as good is an example of the Composite pattern being applied to validation (almost)

There are, of course, tons of frameworks for this. Something clever you could do, that I have used to great effect, is to use an aspect + a validator or make sure whole swaths of new and existing code get checked auto-magically.

@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);
}
}

Here is a snip of 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>

Now all of your controller methods will have that validation code applied here and into the future.

Designing it using exceptions will work, but you will have to write a whole framework to deal with exceptions, many of which can't be handled by your exception interceptor. If you feel the coding itch, then go for it. My advice would be to have different classes of exceptions. Some of them would be critical exceptions, some would be just warnings... you got the picture.

You could (I hope you do) use a proven framework that can handle that beautifully. I speak of JSR 303 and Bean Validation through Spring: http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/validation.html

It takes a while to get used to, but it will pay you back 1000 fold.

I would simply pass around a list of all the errors. The items in the list may not be just exceptions, but rather some objects wrapping more information about the errors, such as name of wrong parameter, its wrong value, position of the error in the string, type of validation (formal, ligical), ID of the error message for localized display to user... Each method on the processing path may append to the list.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top