[JAVA] Mettre en œuvre une validation personnalisée avec des annotations dans Spring Framework et générer un message

Bonjour, c'est Kurata qui joue avec Spring pour la première fois depuis longtemps. Je ne l'ai pas utilisé depuis environ six mois au travail.

Comme le titre l'indique, j'ai noté le contenu suivant.

  1. Créer une annotation
  2. Créez une classe de validateur vers laquelle le traitement de validation est transféré à partir de l'annotation
  3. Afficher les messages personnalisés

introduction

Contexte

Je souhaite vérifier la cohérence avec les données de base stockées dans la base de données.

Ce que vous voulez réaliser

Mettre en place un formulaire de réservation de salle de réunion. Dans le formulaire de réservation de salle de réunion, sélectionnez la salle de réunion à utiliser avec le bouton radio, puis saisissez la date et l'heure d'utilisation et le nombre de personnes. Il y a plusieurs salles de réunion, chacune avec une capacité fixe. Si plus que la capacité est entrée, une erreur d'entrée se produira.

la mise en oeuvre

Spécifications de validation

Obtenez les informations de la salle de réunion en fonction de l'ID de salle de réunion saisi. Obtenez la capacité à partir des informations de la salle de réunion et déterminez si le nombre d'utilisateurs saisi est inférieur ou égal à la capacité. Si la valeur n'est pas valide, un message de validation sera affiché pour le contrôle du formulaire de capacité.

1. Créer une annotation.

Nous l'avons nommé SeatingCapacityValid car il s'agit d'une validation de la capacité. Selon les spécifications de validation, au moins l'ID de la salle et le nom du champ de capacité doivent être reçus en tant qu'argument de l'annotation. Chacun doit être déclaré comme méthode.

SeatingCapacityValid.java


@Documented
@Constraint(validatedBy = {SeatingCapacityValidator.class}) //Spécifiez une classe qui implémente la logique de validation.
@Target(ElementType.TYPE) //Parce que c'est la validation pour plusieurs attributs du formulaire,Désigné pour être accordé à la classe.
@Retention(RetentionPolicy.RUNTIME)
public @interface SeatingCapacityValid{
    //Spécifiez la clé du message à afficher par défaut.
    String message() default "{com.example.demo.form.validator.SeatingCapacityValid.message}";
    String roomIdProperty(); //Recevoir a spécifié le nom de la propriété où l'ID de chambre saisi est stocké.
    String numberOfUsersProperty(); //Recevoir une spécification du nom de la propriété où le nombre d'utilisateurs saisi est stocké.

    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface List {
        SeatingCapacityValid[] value();
    }
}

2. Créez une classe de validateur vers laquelle le traitement de validation est transféré à partir de l'annotation.

Implémentez l'interface ConstraintValidator. Le premier argument de type est une annotation, qui spécifie la cible annotée (dans ce cas, la classe Form, mais pour la polyvalence, le type Object).

SeatingCapacityValidator.java


public class SeatingCapacityValidator implements ConstraintValidator<SeatingCapacityValid, Object> {

    @Autowired
    private RoomRepository roomRepository;
    
    private String roomIdProperty;
    private String timesProperty;
    private String message;
    
    @Override
    public void initialize(CommodityTimesValid constraintAnnotation) {
        roomIdProperty = constraintAnnotation.roomIdProperty();
        numberOfUsersProperty = constraintAnnotation.numberOfUsersProperty();
        message = constraintAnnotation.message();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {        
        BeanWrapper beanWrapper = new BeanWrapperImpl(value);
        Integer roomId = (Integer)beanWrapper.getPropertyValue(roomIdProperty);
        Integer numberOfUsers = (Integer) beanWrapper.getPropertyValue(numberOfUsersProperty);

        if (roomId == null || numberOfUsers == null) {
            return true;
        }

        Optional<Room> mayBeRoom = roomRepository.findOne(roomId);

        return mayBeRoom.map(room -> {
            Integer capacity = room.getCapacity();
            if (capacity >= numberOfUsers) {
                return true;
            //Personnaliser les messages TODO
            context
                    .buildConstraintViolationWithTemplate(message)
                    .addPropertyNode(numberOfUsersProperty)
                    .addConstraintViolation();
            return false;
        }).orElse(true);
    }
}

3. Afficher les messages personnalisés

La logique d'affichage des messages est décrite dans la partie de SeatingCapacityValidator.java écrite comme TODO. Dans le fichier de propriétés, «{0} est défini comme {1} ou moins, et {1} est ciblé pour afficher le nombre de personnes par salle.

SeatingCapacityValidator.java


public class SeatingCapacityValidator implements ConstraintValidator<SeatingCapacityValid, Object> {
    //Omission
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        //Omission
        return mayBeRoom.map(room -> {
            Integer capacity = room.getCapacity();
            if (capacity >= numberOfUsers) {
                return true;
            }
            HibernateConstraintValidatorContext hibernateContext =
                    context.unwrap(HibernateConstraintValidatorContext.class);

            hibernateContext.disableDefaultConstraintViolation();
            hibernateContext
                    .addMessageParameter("1", capacity) //Voici le nombre de personnes dans chaque pièce{1}Mis à.
                    .buildConstraintViolationWithTemplate(message)
                    .addPropertyNode(numberOfUsersProperty)
                    .addConstraintViolation();
            return false;
        }).orElse(true);
    }
}

Note

Lors de la validation à l'aide d'annotations, vous devez être conscient de ce qui suit.

1. La constante spécifiée par l'argument de «@ Target».

Où l'annotation peut-elle être ajoutée? S'agit-il d'une validation de champ unique ou d'un contrôle de corrélation? Est-il vraiment nécessaire de faire une annotation?

Cette fois, je voulais lier la valeur d'entrée de l'écran à un objet qui la place dans la couche de domaine. J'ai donc créé une annotation pour qu'elle soit évaluée au moment de la liaison.

2. La classe spécifiée par l'argument de @ Constraint.

À quelle classe la logique de validation doit-elle être déléguée?

3. Méthodes déclarées avec annotations, arguments d'annotation.

Assurez-vous que les informations nécessaires à la validation proviennent de l'annotation.

4. Comment afficher des informations autres que les arguments d'annotation dans le message.

Les espaces réservés du modèle de message («{0}» ou «{1}») sont remplacés par les informations qui peuvent être obtenues à partir de l'annotation. «{0}» est le nom du champ, et après «{1}», l'argument de l'annotation.

Pour définir autre chose que ce qui précède, utilisez la méthode ConstraintValidatorContext # unwrap (HibernateConstraintValidatorContext.class) pour convertir en la classe HibernateConstraintValidatorContext, puis définissez la chaîne de caractères à afficher.

Le nom de la méthode à utiliser Notation d'espace réservé
addMessageParameter(String s, Object o) {s}
addExpressionVariable(String s, Object o) ${s}

à la fin

Il y a de nombreuses parties que je n'ai pas écrites sur les prérequis, donc je pense que je vais l'organiser lorsque tout le code sera publié.

Recommended Posts

Mettre en œuvre une validation personnalisée avec des annotations dans Spring Framework et générer un message
Changement de beans avec annotation de profil dans Spring
[Rails] J'ai implémenté le message d'erreur de validation avec une communication asynchrone!