Validierung im Spring Boot

1. Übersicht

Wenn es um die Validierung von Benutzereingaben geht, bietet Spring Boot sofort Unterstützung für diese allgemeine, aber wichtige Aufgabe.

Obwohl Spring Boot die nahtlose Integration mit benutzerdefinierten Validatoren unterstützt, ist der De-facto-Standard für die Durchführung der Validierung Hibernate Validator , die Referenzimplementierung des Bean Validation Frameworks.

In diesem Tutorial erfahren Sie, wie Sie Domänenobjekte in Spring Boot überprüfen .

2. Die Maven-Abhängigkeiten

In diesem Fall erfahren Sie, wie Sie Domänenobjekte in Spring Boot validieren, indem Sie einen grundlegenden REST-Controller erstellen.

Der Controller nimmt zuerst ein Domänenobjekt, validiert es dann mit Hibernate Validator und speichert es schließlich in einer speicherinternen H2-Datenbank.

Die Abhängigkeiten des Projekts sind ziemlich Standard:

 org.springframework.boot spring-boot-starter-web   org.springframework.boot spring-boot-starter-data-jpa   com.h2database h2 1.4.197 runtime 

Wie oben gezeigt, haben wir Spring-Boot-Starter-Web in unsere Datei pom.xml aufgenommen , da wir es zum Erstellen des REST-Controllers benötigen. Überprüfen Sie außerdem die neuesten Versionen von Spring-Boot-Starter-JPA und die H2-Datenbank in Maven Central.

Ab Boot 2.3 müssen wir auch die Spring-Boot-Starter-Validierungsabhängigkeit explizit hinzufügen :

 org.springframework.boot spring-boot-starter-validation 

3. Eine einfache Domänenklasse

Da die Abhängigkeiten unseres Projekts bereits vorhanden sind, müssen wir als Nächstes eine Beispiel-JPA-Entitätsklasse definieren, deren Rolle ausschließlich die Modellierung von Benutzern ist.

Werfen wir einen Blick auf diese Klasse:

@Entity public class User { @Id @GeneratedValue(strategy = GenerationType.AUTO) private long id; @NotBlank(message = "Name is mandatory") private String name; @NotBlank(message = "Email is mandatory") private String email; // standard constructors / setters / getters / toString }

Die Implementierung unserer User- Entity-Klasse ist in der Tat ziemlich anämisch, zeigt jedoch auf den Punkt, wie die Einschränkungen von Bean Validation verwendet werden, um die Namen- und E-Mail- Felder einzuschränken .

Der Einfachheit halber haben wir die Zielfelder nur mit der Einschränkung @NotBlank eingeschränkt. Außerdem haben wir die Fehlermeldungen mit dem Nachrichtenattribut angegeben .

Wenn Spring Boot die Klasseninstanz validiert, dürfen die eingeschränkten Felder daher nicht null sein und ihre zugeschnittene Länge muss größer als null sein .

Zusätzlich bietet Bean Validation neben @NotBlank viele andere nützliche Einschränkungen . Dies ermöglicht es uns, verschiedene Validierungsregeln auf die eingeschränkten Klassen anzuwenden und zu kombinieren. Weitere Informationen finden Sie in den offiziellen Dokumenten zur Bohnenvalidierung.

Da wir Spring Data JPA zum Speichern von Benutzern in der speicherinternen H2-Datenbank verwenden, müssen wir auch eine einfache Repository-Schnittstelle definieren, um grundlegende CRUD-Funktionen für Benutzerobjekte bereitzustellen :

@Repository public interface UserRepository extends CrudRepository {}

4. Implementierung eines REST-Controllers

Natürlich müssen wir eine Ebene implementieren, mit der wir die Werte abrufen können , die den eingeschränkten Feldern unseres Benutzerobjekts zugewiesen sind.

Daher können wir sie validieren und abhängig von den Validierungsergebnissen einige weitere Aufgaben ausführen.

Spring Boot macht diesen scheinbar komplexen Prozess durch die Implementierung eines REST-Controllers wirklich einfach .

Schauen wir uns die REST-Controller-Implementierung an:

@RestController public class UserController { @PostMapping("/users") ResponseEntity addUser(@Valid @RequestBody User user) { // persisting the user return ResponseEntity.ok("User is valid"); } // standard constructors / other methods } 

In einem Spring REST-Kontext ist die Implementierung der addUser () -Methode ziemlich Standard.

Der wichtigste Teil ist natürlich die Verwendung der Annotation @Valid .

Wenn Spring Boot ein mit @Valid kommentiertes Argument findet , wird die Standardimplementierung von JSR 380 - Hibernate Validator - automatisch gebootet und das Argument validiert.

Wenn das Zielargument die Validierung nicht besteht, löst Spring Boot eine MethodArgumentNotValidException- Ausnahme aus.

5. Die Annotation @ExceptionHandler

Während es sehr praktisch ist, Spring Boot das Benutzerobjekt automatisch validieren zu lassen, das an die addUser () -Methode übergeben wird, ist die fehlende Facette dieses Prozesses, wie wir die Validierungsergebnisse verarbeiten.

Mit der Annotation @ExceptionHandler können wir bestimmte Arten von Ausnahmen mit einer einzigen Methode behandeln.

Daher können wir es zur Verarbeitung der Validierungsfehler verwenden:

@ResponseStatus(HttpStatus.BAD_REQUEST) @ExceptionHandler(MethodArgumentNotValidException.class) public Map handleValidationExceptions( MethodArgumentNotValidException ex) { Map errors = new HashMap(); ex.getBindingResult().getAllErrors().forEach((error) -> { String fieldName = ((FieldError) error).getField(); String errorMessage = error.getDefaultMessage(); errors.put(fieldName, errorMessage); }); return errors; }

Wir haben die Ausnahme MethodArgumentNotValidException als zu behandelnde Ausnahme angegeben . Folglich ruft Spring Boot diese Methode auf, wenn das angegebene Benutzerobjekt ungültig ist .

Die Methode speichert den Namen und die Fehlermeldung nach der Validierung jedes ungültigen Felds in einer Karte. Als nächstes sendet es die Map als JSON-Darstellung zur weiteren Verarbeitung an den Client zurück.

Einfach ausgedrückt, der REST-Controller ermöglicht es uns, Anforderungen einfach an verschiedene Endpunkte zu verarbeiten, Benutzerobjekte zu validieren und die Antworten im JSON-Format zu senden.

Das Design ist flexibel genug, um Controller-Antworten über mehrere Webebenen hinweg zu verarbeiten, von Vorlagen-Engines wie Thymeleaf bis hin zu einem JavaScript-Framework mit vollem Funktionsumfang wie Angular.

6. Testen des REST-Controllers

Wir können die Funktionalität unseres REST-Controllers einfach mit einem Integrationstest testen.

Beginnen wir damit , die Implementierung der UserRepository- Schnittstelle zusammen mit der UserController- Instanz und einem MockMvc- Objekt zu verspotten / automatisch zu verdrahten :

@RunWith(SpringRunner.class) @WebMvcTest @AutoConfigureMockMvc public class UserControllerIntegrationTest { @MockBean private UserRepository userRepository; @Autowired UserController userController; @Autowired private MockMvc mockMvc; //... } 

Da wir nur die Webebene testen, verwenden wir die Annotation @WebMvcTest . Es ermöglicht uns das einfache Testen von Anforderungen und Antworten mithilfe der statischen Methoden, die von den Klassen MockMvcRequestBuilders und MockMvcResultMatchers implementiert werden.

Testen wir nun die addUser () -Methode mit einem gültigen und einem ungültigen Benutzerobjekt , die im Anforderungshauptteil übergeben werden:

@Test public void whenPostRequestToUsersAndValidUser_thenCorrectResponse() throws Exception { MediaType textPlainUtf8 = new MediaType(MediaType.TEXT_PLAIN, Charset.forName("UTF-8")); String user = "{\"name\": \"bob\", \"email\" : \"[email protected]\"}"; mockMvc.perform(MockMvcRequestBuilders.post("/users") .content(user) .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(MockMvcResultMatchers.status().isOk()) .andExpect(MockMvcResultMatchers.content() .contentType(textPlainUtf8)); } @Test public void whenPostRequestToUsersAndInValidUser_thenCorrectResponse() throws Exception { String user = "{\"name\": \"\", \"email\" : \"[email protected]\"}"; mockMvc.perform(MockMvcRequestBuilders.post("/users") .content(user) .contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(MockMvcResultMatchers.status().isBadRequest()) .andExpect(MockMvcResultMatchers.jsonPath("$.name", Is.is("Name is mandatory"))) .andExpect(MockMvcResultMatchers.content() .contentType(MediaType.APPLICATION_JSON_UTF8)); } } 

Darüber hinaus können wir die REST-Controller-API mit einer kostenlosen API-Lebenszyklus-Testanwendung wie Postman testen .

7. Ausführen der Beispielanwendung

Schließlich können wir unser Beispielprojekt mit einer Standardmethode main () ausführen :

@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean public CommandLineRunner run(UserRepository userRepository) throws Exception { return (String[] args) -> { User user1 = new User("Bob", "[email protected]"); User user2 = new User("Jenny", "[email protected]"); userRepository.save(user1); userRepository.save(user2); userRepository.findAll().forEach(System.out::println); }; } } 

Wie erwartet sollten einige Benutzerobjekte in der Konsole ausgedruckt werden.

Eine POST-Anforderung an den Endpunkt // localhost: 8080 / users mit einem gültigen Benutzerobjekt gibt die Zeichenfolge "Benutzer ist gültig" zurück.

Ebenso gibt eine POST-Anforderung mit einem Benutzerobjekt ohne Name und E-Mail- Werte die folgende Antwort zurück:

{ "name":"Name is mandatory", "email":"Email is mandatory" }

8. Fazit

In diesem Artikel haben wir die Grundlagen der Validierung in Spring Boot kennengelernt .

Wie üblich sind alle in diesem Artikel gezeigten Beispiele auf GitHub verfügbar.