Dynamic DTO Validation Config Aus der Datenbank abgerufen

Dynamic DTO Validation Config Von der Datenbank abgerufen

1. Überblick

In diesem Tutorial werden wir uns ansehen, wie wircreate a custom validation annotation that uses a regular expression retrieved from a database to match against the field value können.

Wir werden den Hibernate Validator als Basisimplementierung verwenden.

2. Maven-Abhängigkeiten

Für die Entwicklung benötigen wir folgende Abhängigkeiten:


    org.springframework.boot
    spring-boot-starter-thymeleaf
    2.0.1.RELEASE


    org.springframework.boot
    spring-boot-starter-data-jpa
    2.0.1.RELEASE

Die neuesten Versionen vonspring-boot-starter-thymeleaf,spring-boot-starter-data-jpa können von Maven Central heruntergeladen werden.

3. Benutzerdefinierte Validierungsanmerkung

In unserem Beispiel erstellen wir eine benutzerdefinierte Anmerkung mit dem Namen@ContactInfo, die einen Wert anhand eines regulären Ausdrucks überprüft, der aus einer Datenbank abgerufen wird. Wir werden diese Validierung dann auf das FeldcontactInfo einer POJO-Klasse namensCustomer anwenden.

Um reguläre Ausdrücke aus einer Datenbank abzurufen, werden diese als EntitätsklasseContactInfoExpressionmodelliert.

3.1. Datenmodelle und Repository

Erstellen wir die KlasseCustomermit den Feldernid undcontactInfo:

@Entity
public class Customer {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;

    private String contactInfo;

    // standard constructor, getters, setters
}

Schauen wir uns als nächstes dieContactInfoExpression-Klasse an, die die Werte für reguläre Ausdrücke in einer Eigenschaft namenspattern enthält:

@Entity
public class ContactInfoExpression {

    @Id
    @Column(name="expression_type")
    private String type;

    private String pattern;

    //standard constructor, getters, setters
}

Als Nächstes fügen wir eine auf Spring Data basierende Repository-Schnittstelle hinzu, um die Entitäten vonContactInfoExpressionzu bearbeiten:

public interface ContactInfoExpressionRepository
  extends Repository {

    Optional findById(String id);
}

3.2. Datenbank-Setup

Zum Speichern regulärer Ausdrücke verwenden wir eine speicherinterneH2-Datenbank mit der folgenden Persistenzkonfiguration:

@EnableJpaRepositories("com.example.dynamicvalidation.dao")
@EntityScan("com.example.dynamicvalidation.model")
@Configuration
public class PersistenceConfig {

    @Bean
    public DataSource dataSource() {
        EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
        EmbeddedDatabase db = builder.setType(EmbeddedDatabaseType.H2)
          .addScript("schema-expressions.sql")
          .addScript("data-expressions.sql")
          .build();
        return db;
    }
}

Die beiden genannten Skripte werden zum Erstellen des Schemas und zum Einfügen der Daten in die Tabellecontact_info_expressionverwendet:

CREATE TABLE contact_info_expression(
  expression_type varchar(50) not null,
  pattern varchar(500) not null,
  PRIMARY KEY ( expression_type )
);

Das Skriptdata-expressions.sqlfügt drei Datensätze hinzu, um die Typenemail,phone, undwebsite darzustellen. Hierbei handelt es sich um reguläre Ausdrücke zum Überprüfen, ob es sich bei diesem Wert um eine gültige E-Mail-Adresse, eine gültige US-Telefonnummer oder eine gültige URL handelt:

insert into contact_info_expression values ('email',
  '[a-z0-9!#$%&*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?')
insert into contact_info_expression values ('phone',
  '^([0-9]( |-)?)?(\(?[0-9]{3}\)?|[0-9]{3})( |-)?([0-9]{3}( |-)?[0-9]{4}|[a-zA-Z0-9]{7})$')
insert into contact_info_expression values ('website',
  '^(http:\/\/www\.|https:\/\/www\.|http:\/\/|https:\/\/)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$')

3.3. Erstellen des benutzerdefinierten Validators

Erstellen wir dieContactInfoValidator-Klasse, die die eigentliche Validierungslogik enthält. Gemäß den Richtlinien für die Java-Validierungsspezifikation überschreibt die Klasseimplements the ConstraintValidator interfacedie MethodeisValid().

Diese Klasse erhält den Wert der aktuell verwendeten Art von Kontaktinformationen -email,phone, oderwebsite -, der in einer Eigenschaft namenscontactInfoType festgelegt ist, und verwendet ihn dann für Rufen Sie den Wert des regulären Ausdrucks aus der Datenbank ab:

public class ContactInfoValidator implements ConstraintValidator {

    private static final Logger LOG = Logger.getLogger(ContactInfoValidator.class);

    @Value("${contactInfoType}")
    private String expressionType;

    private String pattern;

    @Autowired
    private ContactInfoExpressionRepository expressionRepository;

    @Override
    public void initialize(ContactInfo contactInfo) {
        if (StringUtils.isEmptyOrWhitespace(expressionType)) {
            LOG.error("Contact info type missing!");
        } else {
            pattern = expressionRepository.findById(expressionType)
              .map(ContactInfoExpression::getPattern).get();
        }
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if (!StringUtils.isEmptyOrWhitespace(pattern)) {
            return Pattern.matches(pattern, value);
        }
        LOG.error("Contact info pattern missing!");
        return false;
    }
}

Die EigenschaftcontactInfoType kann in der Dateiapplication.properties auf einen der Werteemail,phone oderwebsite gesetzt werden:

contactInfoType=email

3.4. Erstellen der benutzerdefinierten Einschränkungsanmerkung

Und jetzt erstellen wir die Anmerkungsoberfläche für unsere benutzerdefinierte Einschränkung:

@Constraint(validatedBy = { ContactInfoValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RetentionPolicy.RUNTIME)
public @interface ContactInfo {
    String message() default "Invalid value";

    Class[] groups() default {};

    Class[] payload() default {};
}

3.5. Anwenden der benutzerdefinierten Einschränkung

Fügen wir abschließend Validierungsanmerkungen zum FeldcontactInfounserer KlasseCustomerhinzu:

public class Customer {

    // ...
    @ContactInfo
    @NotNull
    private String contactInfo;

    // ...
}

4. Spring Controller und HTML Form

Um unsere Validierungsanmerkung zu testen, erstellen wir eine Spring MVC-Anforderungszuordnung, die die Annotation@Validverwendet, um die Validierung einesCustomer-Objekts auszulösen:

@PostMapping("/customer")
public String validateCustomer(@Valid Customer customer, BindingResult result, Model model) {
    if (result.hasErrors()) {
        model.addAttribute("message", "The information is invalid!");
    } else {
        model.addAttribute("message", "The information is valid!");
    }
    return "customer";
}

Das ObjektCustomerwird von einem HTML-Formular an den Controller gesendet:

Contact Info:

Zum Abschluss können wir unsere Anwendung als Spring Boot-Anwendung ausführen:

@SpringBootApplication
public class DynamicValidationApp {
    public static void main(String[] args) {
        SpringApplication.run(DynamicValidationApp.class, args);
    }
}

5. Fazit

In diesem Beispiel haben wir gezeigt, wie eine benutzerdefinierte Validierungsanmerkung erstellt werden kann, die einen regulären Ausdruck dynamisch aus einer Datenbank abruft und zur Validierung des mit Anmerkungen versehenen Felds verwendet.

Der vollständige Quellcode des Beispiels istover on GitHub.