GNU/Linux >> LINUX-Kenntnisse >  >> Cent OS

So implementieren Sie die Validierung für RESTful-Dienste mit Spring

Frühlingsbohnen-Validierung

Die Datenvalidierung ist kein neues Thema in der Entwicklung von Webanwendungen.

Wir werfen einen kurzen Blick auf die Datenvalidierung im Java-Ökosystem im Allgemeinen und das Spring Framework im Besonderen. Die Java-Plattform war der De-facto-Standard für die Implementierung der Datenvalidierung das ist die Bean Validation-Spezifikation. Die Bean-Validierungsspezifikation hat mehrere Versionen:

  • 1.0 (JSR-303),
  • 1.1 (JSR-349),
  • 2.0 (JSR 380) – die neueste Version

Diese Spezifikation definiert eine Reihe von Komponenten, Schnittstellen und Anmerkungen. Dies bietet eine Standardmethode, um Einschränkungen für die Parameter und Rückgabewerte von Methoden und Parametern von Konstruktoren festzulegen, API bereitzustellen, um Objekte und Objektgraphen zu validieren.

Ein deklaratives Modell wird verwendet, um Objekten und ihren Feldern Einschränkungen in Form von Anmerkungen aufzuerlegen. Es gibt vordefinierte Anmerkungen wie @NotNull , @Digits , @Pattern , @Email , @CreditCard . Es besteht die Möglichkeit, neue benutzerdefinierte Einschränkungen zu erstellen.

Die Validierung kann manuell oder natürlicher ausgeführt werden, wenn andere Spezifikationen und Frameworks Daten zum richtigen Zeitpunkt validieren, z. B. Benutzereingaben, Einfügungen oder Aktualisierungen in JPA.

Validierung in Java-Beispiel

Sehen wir uns in diesem einfachen Bean-Validierungsbeispiel in einer regulären Java-Anwendung an, wie dies in der Praxis umgesetzt werden kann.

Wir haben ein Objekt, das wir validieren möchten, wobei alle Felder mit Einschränkungen versehen sind.

public class SimpleDto {

  @Min(value = 1, message = "Id can't be less than 1 or bigger than 999999")
  @Max(999999)
  private int id;

  @Size(max = 100)
  private String name;

  @NotNull
  private Boolean active;

  @NotNull
  private Date createdDatetime;

  @Pattern(regexp = "^asc|desc$")
  private String order = "asc";

  @ValidCategory(categoryType="simpleDto")
  private String category;
  …
  Constructor, getters and setters

Jetzt können wir es in einer einfachen Java-Anwendung verwenden und das Objekt manuell validieren.

public class SimpleApplication {

  public static void main(String[] args) {

    final SimpleDto simpleDto = new SimpleDto();
    simpleDto.setId(-1);
    simpleDto.setName("Test Name");
    simpleDto.setCategory("simple");
    simpleDto.setActive(true);
    simpleDto.setOrder("asc");
    simpleDto.setCreatedDatetime(new Date());

    ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
    Validator validator = validatorFactory.usingContext().getValidator();

    Set constrains = validator.validate(simpleDto);
    for (ConstraintViolation constrain : constrains) {

       System.out.println(
      "[" + constrain.getPropertyPath() + "][" + constrain.getMessage() + "]"
      );

    }

  }

}

Und das Ergebnis in der Konsole lautet:

“[id] [Id can't be less than 1 or bigger than 999999]”

Validierungseinschränkung und Anmerkung

Erstellen Sie eine benutzerdefinierte Validierungseinschränkung und Anmerkung.


 @Retention(RUNTIME)
   @Target(FIELD)
   @Constraint(validatedBy = {ValidCategoryValidator.class})
   public @interface ValidCategory {

      String categoryType();

      String message() default "Category is not valid";

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

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

    }

And constraint validation implementation:

public class ValidCategoryValidator implements ConstraintValidator<ValidCategory, String> {

    private static final Map<String, List> availableCategories;

    static {

      availableCategories = new HashMap<>();
      availableCategories.put("simpleDto", Arrays.asList("simple", "advanced"));

    }

    private String categoryType;

    @Override
    public void initialize(ValidCategory constraintAnnotation) {

      this.setCategoryType(constraintAnnotation.categoryType());

    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {

      List categories = ValidCategoryValidator.availableCategories.get(categoryType);
      if (categories == null || categories.isEmpty()) {

         return false;

      }

      for (String category : categories) {

         if (category.equals(value)) {

             return true;

      }

    }

    return false;

  }

}

Im obigen Beispiel stammen die verfügbaren Kategorien aus einer einfachen Hash-Map. In realen Anwendungsfällen können sie aus der Datenbank oder anderen Diensten abgerufen werden.

Bitte beachten Sie, dass Einschränkungen und Validierungen nicht nur auf Feldebene, sondern auch für ein ganzes Objekt angegeben und durchgeführt werden können.

Wenn wir verschiedene Feldabhängigkeiten validieren müssen, z. B. das Startdatum, darf es nicht nach dem Enddatum liegen.

Die am häufigsten verwendeten Implementierungen der Bean-Validierung Spezifikationen sind Hibernate Validator und Apache BVal .

Validierung mit Spring

Das Spring-Framework bietet mehrere Funktionen für die Validierung.

  • Unterstützung für Bean Validation API-Versionen 1.0, 1.1 (JSR-303, JSR-349) wurde in Spring Framework ab Version 3 eingeführt.
  • Spring hat seine eigene Validator-Schnittstelle, die sehr einfach ist und in einer bestimmten DataBinder-Instanz eingestellt werden kann. Dies könnte nützlich sein, um eine Validierungslogik ohne Anmerkungen zu implementieren.

Bean-Validierung mit Spring

Spring Boot bietet eine gestartete Validierung, die in das Projekt aufgenommen werden kann:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
 </dependency>

Dieser Starter bietet eine Version von Hibernate Validator kompatibel mit dem aktuellen Spring Boot.

Mithilfe der Bean-Validierung könnten wir einen Anforderungstext, Abfrageparameter und Variablen innerhalb des Pfads validieren (z. B. / /simpledto/{id} ) oder beliebige Methoden- oder Konstruktorparameter.

POST- oder PUT-Anforderungen

In POST- oder PUT-Anforderungen übergeben wir beispielsweise die JSON-Nutzlast, Spring konvertiert sie automatisch in ein Java-Objekt und jetzt möchten wir das resultierende Objekt validieren. Lassen Sie uns SimpleDto verwenden Objekt aus Beispiel 1:

@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {

    @Autowired
    private SimpleDtoService simpleDtoService;

    @RequestMapping(path = "", method = RequestMethod.POST, produces = 
    "application/json")
    public SimpleDto createSimpleDto(

        @Valid @RequestBody SimpleDto simpleDto) {

      SimpleDto result = simpleDtoService.save(simpleDto);

      return result;

    }

}

Wir haben gerade @Valid hinzugefügt Anmerkung zu SimpleDto Parameter, der mit @RequestBody kommentiert ist . Dadurch wird Spring angewiesen, die Validierung zu verarbeiten, bevor ein tatsächlicher Methodenaufruf durchgeführt wird. Falls die Validierung fehlschlägt, löst Spring eine MethodArgument NotValidException aus die standardmäßig eine 400-Antwort (Bad Request) zurückgibt.

Validieren von Pfadvariablen

Das Validieren von Pfadvariablen funktioniert etwas anders. Das Problem ist, dass wir Constraint-Anmerkungen jetzt direkt zu Methodenparametern hinzufügen müssen, anstatt innerhalb von Objekten.

Damit dies funktioniert, gibt es 2 mögliche Optionen:

Option 1:@Validierte Anmerkung

Fügen Sie @Validated hinzu Annotation an den Controller auf Klassenebene, um Constraint-Annotationen für Methodenparameter auszuwerten.

Option 2:Pfadvariable

Verwenden Sie ein Objekt, das die Pfadvariable darstellt, wie im folgenden Beispiel gezeigt:

@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {

    @Autowired
    private SimpleDtoService simpleDtoService;

    @RequestMapping(path = "/{simpleDtoId}", method = RequestMethod.GET, produces = 
    "application/json")
    public SimpleDto getSimpleDto(

      @Valid SimpleDtoIdParam simpleDtoIdParam) {

    SimpleDto result = simpleDtoService.findById(simpleDtoIdParam.getSimpleDtoId());

    if (result == null) {

      throw new NotFoundException();

    }

    return result;

  }

}

In diesem Fall haben wir SimpleDtoIdParam Klasse, die simpleDtoId enthält Feld, das anhand einer standardmäßigen oder benutzerdefinierten Bean-Einschränkungsanmerkung validiert wird. Name der Pfadvariablen (/{simpleDtoId} ) sollte mit dem Feldnamen identisch sein (damit Spring den Setter für dieses Feld finden kann).

private static final long serialVersionUID = -8165488655725668928L;

    @Min(value = 1)
    @Max(999999)
    private int simpleDtoId;

    public int getSimpleDtoId() {
    return simpleDtoId;
    }

    public void setSimpleDtoId(int simpleDtoId) {
    this.simpleDtoId = simpleDtoId;
    }

}

Im Gegensatz zu Request Body Validierung, Pfadvariable Validierung löst ConstraintViolationException aus anstelle von MethodArgumentNotValidException . Daher müssen wir einen benutzerdefinierten Ausnahmehandler erstellen.

Das Java-Spring-Framework ermöglicht auch die Validierung von Parametern auf Serviceebene mit @Validated Anmerkung (Klassenebene) und @Valid (Parameterebene).

In Anbetracht der Tatsache, dass Spring JPA darunter Hibernate verwendet, unterstützt es auch die Bean-Validierung für Entitätsklassen. Bitte beachten Sie, dass es in vielen Fällen wahrscheinlich keine gute Idee ist, sich auf diese Validierungsebene zu verlassen, da dies bedeutet, dass die gesamte Logik zuvor mit ungültigen Objekten umgegangen ist.

Spring Validation Interface

Spring definiert eine eigene Schnittstelle für den Validierungs-Validator (org.springframework.validation.Validator). Es kann für eine bestimmte DataBinder-Instanz festgelegt werden und die Validierung ohne Anmerkungen implementieren (nicht deklarativer Ansatz).

Um diesen Ansatz zu implementieren, müssten wir:

  1. Implementieren Sie die Validator-Schnittstelle
  2. Validierer hinzufügen

Validator-Schnittstelle implementieren

Implementieren Sie die Validator-Schnittstelle, zum Beispiel können Sie mit unserem SimpleDto arbeiten Klasse:

@Component
public class SpringSimpleDtoValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
    return SimpleDto.class.isAssignableFrom(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {

      if (errors.getErrorCount() == 0) {

        SimpleDto param = (SimpleDto) target;
        Date now = new Date();
        if (param.getCreatedDatetime() == null) {

          errors.reject("100",

            "Create Date Time can't be null");

        } else if (now.before(param.getCreatedDatetime())) {

          errors.reject("101",

            "Create Date Time can't be after current date time");

        }

      }

    }

}

Überprüfen Sie hier, ob die erstellte Datetime Zeitstempel liegt in der Zukunft.

Validator hinzufügen

Validator-Implementierung zu DataBinder hinzufügen :

@RestController
@RequestMapping("/simpledto")
public class SimpleDtoController {

    @Autowired
    private SimpleDtoService simpleDtoService;

    @Autowired
    private SpringSimpleDtoValidator springSimpleDtoValidator;

    @InitBinder("simpleDto")
    public void initMerchantOnlyBinder(WebDataBinder binder) {
    binder.addValidators(springSimpleDtoValidator);
    }

    @RequestMapping(path = "", method = RequestMethod.POST, produces = 
    "application/json")
    public SimpleDto createSimpleDto(

      @Valid @RequestBody SimpleDto simpleDto) {

    SimpleDto result = simpleDtoService.save(simpleDto);
    return result;

  }

}

Jetzt haben wir SimpleDto validiert mit Constraint-Annotationen und unserer benutzerdefinierten Spring Validation-Implementierung.


Cent OS
  1. So verwalten Sie Systemd-Dienste mit Systemctl unter Linux

  2. So scannen Sie mit Rkhunter einen Debian-Server nach Rootkits

  3. So verwalten Sie Linux-Dienste mit dem Befehl systemctl

  4. Wie erstelle ich eine For-Schleife mit einer veränderbaren Anzahl von Iterationen?

  5. So konfigurieren Sie IMAP mit SSL

So integrieren Sie Grafana mit Prometheus zur Überwachung

So scannen Sie mit ClamAV unter Ubuntu 20.04 nach Viren

So führen Sie Pods als systemd-Dienste mit Podman aus

So konfigurieren Sie Nginx mit SSL

So richten Sie Redis für Hochverfügbarkeit mit Sentinel in CentOS 8 ein – Teil 2

So automatisieren Sie Docker-Sicherheitsaudits mit Docker Bench for Security