Wie kann die Parametervalidierung standardmäßig mithilfe der Verantwortlichkeitskette elegant durchgeführt werden?

Dieser Artikel nimmt am "Golden Stone Project" teil

Vorwort

Die Parameterüberprüfung im Projekt ist sehr wichtig, sie kann die Sicherheit und Rechtmäßigkeit unserer Anwendung schützen. Ich denke, jeder macht normalerweise so etwas:

@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
}
复制代码

Der größte Vorteil dabei ist, dass es einfach und direkt ist, aber wenn die Verifizierungslogik komplex ist, wird die Klasse sehr groß, und das Obige besteht darin, den Codeausführungsprozess durch das Auslösen von Ausnahmen zu ändern, was ebenfalls nicht empfohlen wird.

Welchen besseren Weg gibt es also, um Parameter zu überprüfen? Dieser Artikel empfiehlt eine Möglichkeit, die Parameterüberprüfungsfunktion elegant durch das Entwurfsmuster der Verantwortungskette zu implementieren.Wir verwenden ein Beispiel für die Benutzerregistrierung, um zu verstehen, wie sie implementiert wird.

  • Gültige Registrierungsdaten - Vorname, Nachname, E-Mail, Benutzername und Passwort.
  • Der Benutzername muss eindeutig sein.
  • E-Mail muss eindeutig sein.

Definieren Sie Benutzerregistrierungs- und Authentifizierungsergebnisklassen

  1. Definieren Sie eine SignUpCommandKlasse, um die vom Benutzer registrierten Attributinformationen zu akzeptieren. Und verwenden Sie @ValueAnmerkungen , um diese Klasse unveränderlich zu machen.
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;
复制代码
  • Verwenden Sie javax.validationAnmerkungen, um @NotBlankzu @Sizeüberprüfen, ob die Benutzerregistrierungsinformationen gültig sind.
  • lombokAnmerkung verwendet, weil @Valueich möchte, dass das Befehlsobjekt unveränderlich ist. Die Daten des registrierten Benutzers sollten mit den im Registrierungsformular ausgefüllten Daten übereinstimmen.
  1. Definieren Sie eine Klasse, die Validierungsergebnisse ValidationResultwie folgt speichert:
@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;
    }
}
复制代码
  • Meiner Meinung nach ist dies ein sehr praktischer Methodenrückgabetyp und besser als das Auslösen einer Ausnahme mit einer Validierungsnachricht.
  1. Da es sich um eine Verantwortlichkeitskette handelt, müssen wir auch eine „Ketten“-Klasse definieren ValidationStep, die die Oberklasse dieser Verifizierungsschritte ist, und wir wollen sie miteinander „verknüpfen“.
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);
    }
}
复制代码

Kern-Validierungslogik

Jetzt beginnen wir mit der Kernlogik der Parameterverifikation, also wie man die oben definierten Klassen hintereinander schaltet.

  1. Wir definieren eine Schnittstellenklasse für die RegistrierungsüberprüfungSignUpValidationService
public interface SignUpValidationService {
    ValidationResult validate(SignUpCommand command);
}
复制代码
  1. Jetzt können wir die oben definierten Klassen und das Chain of Responsibility-Muster verwenden, um es einfach zu implementieren. Der Code lautet wie folgt:
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);
        }
    }
}
复制代码
  • validateDie Methode ist die Kernmethode, die linkWithden Kettenvalidator der Methodenassemblyparameter aufruft, die mehrere Überprüfungsklassen umfasst. Führen Sie zuerst die grundlegende Überprüfung durch, falls bestanden, um zu überprüfen, ob der Benutzername wiederholt wird, falls auch bestanden, um zu überprüfen, ob dies der Fall ist wird wiederholt Email.
  • CommandConstraintsValidationStepKlasse, dieser Schritt ist eine grundlegende Überprüfung, es javax validation annotationwird alles überprüft, z. B. ob es leer ist, Emailob das Format korrekt ist usw. Das ist sehr praktisch, wir müssen diese Validatoren nicht selbst schreiben. Wenn ein Objekt gültig ist, lässt der Aufruf checkNextder Methode den Prozess zum nächsten Schritt übergehen, checkNextwenn nicht, ValidationResultkehrt er sofort zurück.
  • UsernameDuplicationValidationStepKlasse, dieser Schritt überprüft, ob der Benutzername wiederholt wird, muss hauptsächlich die Datenbank überprüfen. Wenn dies der Fall ist, wird es sofort ungültig zurückgegeben ValidationResult. Andernfalls gehen Sie weiter zurück und überprüfen Sie den nächsten Schritt.
  • EmailDuplicationValidationStepKlasse, E-Mail-Wiederholungsbestätigung. Da es keinen nächsten Schritt gibt, wird die E-Mail zurückgegeben, wenn sie eindeutig ist ValidationResult.valid().

Zusammenfassen

Das Obige ist der vollständige Prozess zur Realisierung unserer Parameterverifizierung durch den Modus der Verantwortungskette. Haben Sie es gelernt? Diese Methode kann die Verifizierungslogik elegant in eine separate Klasse aufteilen. Wenn Sie eine neue Verifizierungslogik hinzufügen, müssen Sie nur eine neue hinzufügen Klasse, und dann in der "Verifizierungskette" zusammengesetzt. Das ist meiner Meinung nach aber eher für relativ komplexe Szenarien geeignet, da es sich bei einer einfachen Verifizierung gar nicht braucht, aber die Komplexität des Codes erhöht.

Willkommen, um auf den Austausch und das Studium des persönlichen öffentlichen Kontos [JAVA Xuyang] zu achten

Ich denke du magst

Origin juejin.im/post/7215386649676791867
Empfohlen
Rangfolge