Cet article participe au "Golden Stone Project"
avant-propos
La vérification des paramètres dans le projet est très importante, elle peut protéger la sécurité et la légalité de notre application. Je pense que tout le monde fait généralement quelque chose comme ça:
@Override
public void validate(SignUpCommand command) {
validateCommand(command); // will throw an exception if command is not valid
validateUsername(command.getUsername()); // will throw an exception if username is duplicated
validateEmail(commend.getEmail()); // will throw an exception if email is duplicated
}
复制代码
Le plus grand avantage de cela est qu'il est simple et direct, mais si la logique de vérification est complexe, la classe deviendra très grande, et ce qui précède consiste à modifier le processus d'exécution du code en levant des exceptions, ce qui n'est pas non plus recommandé.
Alors quoi de mieux pour vérifier les paramètres ? Cet article recommande une manière élégante d'implémenter la fonction de vérification des paramètres via le modèle de conception de la chaîne de responsabilité. Nous utilisons un exemple d'enregistrement d'utilisateur pour comprendre comment l'implémenter.
- Données d'inscription valides - prénom, nom, e-mail, nom d'utilisateur et mot de passe.
- Le nom d'utilisateur doit être unique.
- L'e-mail doit être unique.
Définir les classes de résultats d'enregistrement et d'authentification des utilisateurs
- Définissez une
SignUpCommand
classe pour accepter les informations d'attribut enregistrées par l'utilisateur. Et utilisez@Value
des annotations pour rendre cette classe immuable.
import lombok.Value;
import javax.validation.constraints.*;
@Value
public class SignUpCommand {
@Min(2)
@Max(40)
@NotBlank
private final String firstName;
@Min(2)
@Max(40)
@NotBlank
private final String lastName;
@Min(2)
@Max(40)
@NotBlank
private final String username;
@NotBlank
@Size(max = 60)
@Email
private final String email;
@NotBlank
@Size(min = 6, max = 20)
private final String rawPassword;
复制代码
- Utilisez
javax.validation
des annotations telles que@NotBlank
pour@Size
vérifier si les informations d'enregistrement de l'utilisateur sont valides. lombok
Annotation utilisée parce que@Value
je veux que l'objet de commande soit immuable. Les données de l'utilisateur enregistré doivent être les mêmes que les données renseignées dans le formulaire d'inscription.
- Définissez une classe qui stocke les résultats de validation
ValidationResult
comme suit :
@Value
public class ValidationResult {
private final boolean isValid;
private final String errorMsg;
public static ValidationResult valid() {
return new ValidationResult(true, null);
}
public static ValidationResult invalid(String errorMsg) {
return new ValidationResult(false, errorMsg);
}
public boolean notValid() {
return !isValid;
}
}
复制代码
- À mon avis, c'est un type de retour de méthode très pratique, et mieux que de lancer une exception avec un message de validation.
- Puisqu'il s'agit d'une chaîne de responsabilité, nous devons également définir une classe "chaîne"
ValidationStep
, qui est la superclasse de ces étapes de vérification, et nous voulons les "lier" les unes aux autres.
public abstract class ValidationStep<T> {
private ValidationStep<T> next;
public ValidationStep<T> linkWith(ValidationStep<T> next) {
if (this.next == null) {
this.next = next;
return this;
}
ValidationStep<T> lastStep = this.next;
while (lastStep.next != null) {
lastStep = lastStep.next;
}
lastStep.next = next;
return this;
}
public abstract ValidationResult validate(T toValidate);
protected ValidationResult checkNext(T toValidate) {
if (next == null) {
return ValidationResult.valid();
}
return next.validate(toValidate);
}
}
复制代码
Logique de validation de base
Nous commençons maintenant la logique de base de la vérification des paramètres, c'est-à-dire comment connecter les classes définies ci-dessus en série.
- Nous définissons une classe d'interface pour la vérification de l'enregistrement
SignUpValidationService
public interface SignUpValidationService {
ValidationResult validate(SignUpCommand command);
}
复制代码
- Nous pouvons maintenant utiliser les classes définies ci-dessus et le modèle Chain of Responsibility pour l'implémenter facilement, le code est le suivant :
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import java.util.Set;
@Service
@AllArgsConstructor
public class DefaultSignUpValidationService implements SignUpValidationService {
private final UserRepository userRepository;
@Override
public ValidationResult validate(SignUpCommand command) {
return new CommandConstraintsValidationStep()
.linkWith(new UsernameDuplicationValidationStep(userRepository))
.linkWith(new EmailDuplicationValidationStep(userRepository))
.validate(command);
}
private static class CommandConstraintsValidationStep extends ValidationStep<SignUpCommand> {
@Override
public ValidationResult validate(SignUpCommand command) {
try (ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory()) {
final Validator validator = validatorFactory.getValidator();
final Set<ConstraintViolation<SignUpCommand>> constraintsViolations = validator.validate(command);
if (!constraintsViolations.isEmpty()) {
return ValidationResult.invalid(constraintsViolations.iterator().next().getMessage());
}
}
return checkNext(command);
}
}
@AllArgsConstructor
private static class UsernameDuplicationValidationStep extends ValidationStep<SignUpCommand> {
private final UserRepository userRepository;
@Override
public ValidationResult validate(SignUpCommand command) {
if (userRepository.findByUsername(command.getUsername()).isPresent()) {
return ValidationResult.invalid(String.format("Username [%s] is already taken", command.getUsername()));
}
return checkNext(command);
}
}
@AllArgsConstructor
private static class EmailDuplicationValidationStep extends ValidationStep<SignUpCommand> {
private final UserRepository userRepository;
@Override
public ValidationResult validate(SignUpCommand command) {
if (userRepository.findByEmail(command.getEmail()).isPresent()) {
return ValidationResult.invalid(String.format("Email [%s] is already taken", command.getEmail()));
}
return checkNext(command);
}
}
}
复制代码
validate
La méthode est la méthode principale, qui appellelinkWith
le validateur de chaîne des paramètres d'assemblage de la méthode, ce qui implique plusieurs classes de vérification, effectuez d'abord la vérification de base, si elle est transmise, pour vérifier si le nom d'utilisateur est répété, si elle est également transmise, pour vérifier s'il est répétéEmail
.CommandConstraintsValidationStep
Classe, cette étape est une vérification de base, toutjavax validation annotation
sera vérifié, comme s'il est vide,Email
si le format est correct, etc. C'est très pratique, nous n'avons pas à écrire ces validateurs nous-mêmes. Si un objet est valide, l'appelcheckNext
de la méthode permet au processus de passer à l'étape suivante,checkNext
sinon,ValidationResult
il reviendra immédiatement.UsernameDuplicationValidationStep
Classe, cette étape vérifie si le nom d'utilisateur est répété, principalement besoin de vérifier la base de données. Si c'est le cas, il retournera invalide immédiatementValidationResult
, sinon, continuez à revenir en arrière et vérifiez l'étape suivante.EmailDuplicationValidationStep
classe, vérification répétée par e-mail. Puisqu'il n'y a pas d'étape suivante, si l'e-mail est unique, il sera renvoyéValidationResult.valid()
.
Résumer
Ce qui précède est le processus complet de réalisation de notre vérification des paramètres via le mode chaîne de responsabilité. L'avez-vous appris ? Cette méthode peut diviser élégamment la logique de vérification en une classe distincte. Si vous ajoutez une nouvelle logique de vérification, il vous suffit d'ajouter une nouvelle classe, puis assemblés dans la "chaîne de vérification". Mais à mon avis, cela convient mieux à des scénarios relativement complexes. S'il ne s'agit que d'une simple vérification, il n'est pas nécessaire de le faire du tout, mais cela augmentera la complexité du code.
Bienvenue à prêter attention à l'échange et à l'étude du compte public personnel [JAVA Xuyang]