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
- Definieren Sie eine
SignUpCommand
Klasse, um die vom Benutzer registrierten Attributinformationen zu akzeptieren. Und verwenden Sie@Value
Anmerkungen , 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.validation
Anmerkungen, um@NotBlank
zu@Size
überprüfen, ob die Benutzerregistrierungsinformationen gültig sind. lombok
Anmerkung verwendet, weil@Value
ich möchte, dass das Befehlsobjekt unveränderlich ist. Die Daten des registrierten Benutzers sollten mit den im Registrierungsformular ausgefüllten Daten übereinstimmen.
- Definieren Sie eine Klasse, die Validierungsergebnisse
ValidationResult
wie 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.
- 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.
- Wir definieren eine Schnittstellenklasse für die Registrierungsüberprüfung
SignUpValidationService
public interface SignUpValidationService {
ValidationResult validate(SignUpCommand command);
}
复制代码
- 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);
}
}
}
复制代码
validate
Die Methode ist die Kernmethode, dielinkWith
den 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 wiederholtEmail
.CommandConstraintsValidationStep
Klasse, dieser Schritt ist eine grundlegende Überprüfung, esjavax validation annotation
wird alles überprüft, z. B. ob es leer ist,Email
ob 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 AufrufcheckNext
der Methode den Prozess zum nächsten Schritt übergehen,checkNext
wenn nicht,ValidationResult
kehrt er sofort zurück.UsernameDuplicationValidationStep
Klasse, 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ückgegebenValidationResult
. Andernfalls gehen Sie weiter zurück und überprüfen Sie den nächsten Schritt.EmailDuplicationValidationStep
Klasse, E-Mail-Wiederholungsbestätigung. Da es keinen nächsten Schritt gibt, wird die E-Mail zurückgegeben, wenn sie eindeutig istValidationResult.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