How to customize validation error behavior in Atoti Limits by implementing IValidationErrorHandler to control how errors are collected and reported, and IValidationError to define custom error structures
Whether you are using the default validators or have implemented custom ones,
you can customize the behavior that occurs when validation fails, as well as create custom errors,
for limit structures, limits, and incidents by adding beans that implement the
ILimitStructureValidationErrorHandler, ILimitValidationErrorHandler, and IIncidentValidationErrorHandler
interfaces, respectively.
These interfaces provide the explicit type parameter for the generically-typed IValidationErrorHandler
interface:
public interface IValidationErrorHandler<T extends IValidated & ILimitsManagedObject> { /** * Handle a validation failure (i.e. a validation error not caused by an exception) * * @param action recommended action to take to resolve the error * @param reason human-readable cause of the error */ void handleValidationFailure(String action, String reason); /** * Handle a validation error (i.e. a validation error that is caused by an exception) * * @param action recommended action to take to resolve the error * @param ex the exception that caused the error */ void handleValidationError(String action, Exception ex); /** * Handle a validation error * * @param validationError the validation error to handle */ void handleValidationError(IValidationError<T> validationError); /** * @return the list of validation errors */ List<IValidationError> getValidationErrors();}
DefaultValidationErrorHandler<T> is a generic implementation of IValidationErrorHandler and is
used by ILimitStructureValidationErrorHandler, ILimitValidationErrorHandler, and
IIncidentValidationErrorHandler.This default implementation does the following:
It collects all errors that occur during validation.
It prints an error report in the logs from the postValidation() method of the validator.
If throwException is true when reset() is called, it returns a LimitsDefinitionalValidationRuntimeException. This is a ProblemDetail object that
contains all the validation errors in its validationErrors property, including the index and field name for each error.
If you are implementing a custom error handler we recommend extending this class.
To implement a custom limit error handler that logs all errors as they occur and continues processing, you can do the
following:
In addition to customizing the behavior when a validation error occurs, you can also customize the validation errors themselves.
The IValidationError interface allows you to define a custom validation error:
public interface IValidationError<T extends IValidated> { /** * @return the human-readable cause of the error */ String getReason(); /** * @return the human-readable action to take to resolve the error */ String getAction(); /** * @return the underlying exception that caused the error */ Throwable getException(); /** * @return the 0-based index of the object in the list of objects processed by the validator. If * the source of the object is a CSV file, add 2 to this value to determine the line number of * the record in the file. */ int getIndex(); /** * @return the name of the field containing the error */ String getFieldName();}
The default implementation of IValidationError is DefaultValidationError. This default is generic for all types of components that
are validated, and uses Lombok’s @AllArgsConstructor and @Getter annotations to provide its functionality.To create a custom validation error specific to incidents that had some structured formatting and
default action, you can do the following:
public class MyIncidentValidationError implements IValidationError<Incident> { public static final String REASON_TEMPLATE = "Validation failed for Incident due to: %s"; public static final String DEFAULT_ACTION = "Please check the incident details and try again."; protected String action; protected String reason; protected Throwable exception; protected int index; protected String fieldName; @Override public String getReason() { return String.format(REASON_TEMPLATE, reason); } @Override public String getAction() { return action == null ? DEFAULT_ACTION : action; } @Override public Throwable getException() { return exception; } @Override public int getIndex() { return index; } @Override public String getFieldName() { return fieldName; }}