Dieses Mal werde ich vorstellen, wie eine Validierung (Eingabeprüfung) für den Wert durchgeführt wird, der von der von Spring Data bereitgestellten "Pageable" -Schnittstelle gehalten wird. Unter Verwendung von "Pageable", das von Spring Data bereitgestellt wird, können zusätzlich zu den zu durchsuchenden Seiteninformationen (Anzahl der Seiten, Anzahl der auf der Seite angezeigten Elemente) Sortierbedingungen (Zieleigenschaft / Spalte, aufsteigende / absteigende Reihenfolge) in den Anforderungsparametern angegeben werden. Es ist funktional praktisch, die Sortierbedingung angeben zu können. Wenn Sie sie jedoch falsch verwenden, kann dies zu einer SQL-Injection usw. führen. Was benötigt wird, um diese Art der Injektion zu verhindern, ist ... natürlich ... Validierung.
Überprüfen Sie die Argumente der im Controller implementierten Handler-Methode mithilfe der von Spring bereitgestellten Funktion Method Validation (unter Verwendung des Bean Validation-Mechanismus).
Insbesondere ... sieht es so aus.
@GetMapping
Page<Todo> search(@AllowedSortProperties({"id", "title"}) Pageable pageable) {
// ...
return page;
}
Die von Spring bereitgestellte Methodenvalidierung verwendet die Funktionalität von Spring AOP, um die Validierung von Methodenaufrufargumenten und Rückgabewerten zu unterstützen.
Wenden Sie hier eine Validierung an, die prüft, ob sich die Zieleigenschaft oder Spalte der Sortierbedingung in der Zulassungsliste befindet.
Erstellen Sie wie immer ein Spring Boot-Entwicklungsprojekt aus "SPRING INITIALIZR". Wählen Sie in diesem Fall "Web" als Abhängigkeitsartefakt.
Fügen Sie den abhängigen Bibliotheken Spring Data Commons hinzu, das "Pageable" enthält.
pom.xml
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
</dependency>
Wenn Sie Spring Data Commons zu Ihren abhängigen Bibliotheken hinzufügen, aktiviert der AutoConfigure-Mechanismus von Spring Boot Funktionen, mit denen Sie "Pageable" als Argument für die Handler-Methode des Controllers angeben können (z. B. "PageableHandlerMethodArgumentResolver").
Erstellen Sie eine "Constraint Annotation" und eine "Implementierungsklasse für" ConstraintValidator ", um nach" Pageable "zu suchen.
Einschränkungsanmerkung für Pageable
package com.example;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
@Documented
@Constraint(validatedBy = {AllowedSortPropertiesValidator.class})
@Target({ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
public @interface AllowedSortProperties {
String message() default "{com.example.AllowedSortProperties.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String[] value();
@Target({ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@interface List {
AllowedSortProperties[] value();
}
}
Implementierungsklasse von ConstraintValidator for Pageable
package com.example;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
public class AllowedSortPropertiesValidator implements ConstraintValidator<AllowedSortProperties, Pageable> {
private Set<String> allowedSortProperties;
@Override
public void initialize(AllowedSortProperties constraintAnnotation) {
this.allowedSortProperties = new HashSet<>(Arrays.asList(constraintAnnotation.value()));
}
@Override
public boolean isValid(Pageable value, ConstraintValidatorContext context) {
if (value == null || value.getSort() == null) {
return true;
}
if (allowedSortProperties.isEmpty()) {
return true;
}
for (Sort.Order order : value.getSort()) {
if (!allowedSortProperties.contains(order.getProperty())) {
return false;
}
}
return true;
}
}
Wenden Sie die Methodenvalidierung auf den Handler-Methodenaufruf des Controllers an.
package com.example;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
@SpringBootApplication
public class BeanValidationDemoApplication {
public static void main(String[] args) {
SpringApplication.run(BeanValidationDemoApplication.class, args);
}
@Bean //Bean-Definition der Komponente, die den Validator der Bean-Validierung generiert
static LocalValidatorFactoryBean localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
@Bean // Method Validation(AOP)Bean Definition der Komponente, zu der
static MethodValidationPostProcessor methodValidationPostProcessor(LocalValidatorFactoryBean localValidatorFactoryBean) {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.setValidator(localValidatorFactoryBean);
return processor;
}
}
Erstellen Sie eine Handler-Methode, die "Pageable" empfängt, und geben Sie die in ↑ erstellte Einschränkungsanmerkung an. Zu diesem Zeitpunkt muss der Klassenebene "@ Validated" hinzugefügt werden, um das Ziel der Methodenvalidierung zu sein.
package com.example;
import org.springframework.data.domain.Pageable;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Validated //Wenn Sie dies nicht hinzufügen, wird es nicht der Methodenvalidierung unterzogen
@RestController
@RequestMapping("/todos")
public class TodoRestController {
@GetMapping
Pageable search(@AllowedSortProperties({"id", "title"}) Pageable pageable) {
return pageable;
}
}
Wenn dies der Fall ist, wird der Suchvorgang ausgeführt und die Liste der Domänenobjekte zurückgegeben. Hier erfolgt die Implementierung jedoch so, dass das empfangene "Pageable" so zurückgegeben wird, wie es ist. (Da es sich nur um eine Verifizierungsanwendung handelt ...: heat_smile :)
Lassen Sie uns die Spring Boot-Anwendung starten und tatsächlich darauf zugreifen.
Verhalten, wenn ein Eigenschaftsspaltenname in der Zulassungsliste angegeben wird
$ curl -D - http://localhost:8080/todos?sort=id
HTTP/1.1 200
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 17 Dec 2016 15:51:51 GMT
{"sort":[{"direction":"ASC","property":"id","ignoreCase":false,"nullHandling":"NATIVE","ascending":true}],"offset":0,"pageNumber":0,"pageSize":20}
Verhalten, wenn ein Eigenschaftsspaltenname angegeben wird, der nicht in der Zulassungsliste enthalten ist
$ curl -D - http://localhost:8080/todos?sort=createdAt
HTTP/1.1 500
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sat, 17 Dec 2016 15:53:47 GMT
Connection: close
{"timestamp":1481990027135,"status":500,"error":"Internal Server Error","exception":"javax.validation.ConstraintViolationException","message":"No message available","path":"/todos"}
Es scheint gut zu funktionieren, aber der Fehler beim Angeben eines Eigenschaftsspaltennamens, der nicht in der Zulassungsliste enthalten ist, wird als 500 Internal Server Error behandelt. Normalerweise sollte es 400 Bad Request sein.
Im Standardzustand ist es 500 Internal Server Error. Fügen wir also die Ausnahmebehandlung hinzu, wenn bei der Methodenüberprüfung ein Fehler auftritt, der 400 Bad Request ergibt.
package com.example;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import javax.validation.ConstraintViolationException;
@Validated
@RestController
@RequestMapping("/todos")
public class TodoRestController {
@GetMapping
Pageable search(@AllowedSortProperties({"id", "title"}) Pageable pageable) {
return pageable;
}
@ExceptionHandler //Methode zur Behandlung der ConstraintViolationException hinzugefügt
@ResponseStatus(HttpStatus.BAD_REQUEST)
String handleConstraintViolationException(ConstraintViolationException e) {
return "Detect invalid parameter.";
}
}
Ursprünglich sollte es im globalen Ausnahmebehandler implementiert sein, aber hier ist es im Controller implementiert. Außerdem wird die Fehlerantwort so implementiert, dass nur eine einfache Fehlermeldung ausgegeben wird. Implementieren Sie sie daher gemäß Ihren Anforderungen.
Fehlerantwort nach Hinzufügen der Ausnahmebehandlung
$ curl -D - http://localhost:8080/todos?sort=createdAt
HTTP/1.1 400
Content-Type: text/plain;charset=UTF-8
Content-Length: 25
Date: Sat, 17 Dec 2016 16:02:29 GMT
Connection: close
Detect invalid parameter.
Note:
Wenn die Anwendung gut gestaltet ist, gibt der Benutzer die von "Pageable" verwalteten Informationen nicht manuell ein. Daher halte ich es nicht für erforderlich, eine freundliche Fehlerantwort zurückzugeben, wenn ein Fehler erkannt wird. (Diese Art von Fehler sollte nicht auftreten, wenn Sie die von der Anwendung bereitgestellte Funktion verwenden ...)
Dieses Mal habe ich vorgestellt, wie die von Spring Data bereitgestellte "Pageable" -Schnittstelle validiert wird. Ich weiß nicht, ob es die beste Vorgehensweise ist, die Methodenvalidierung zu verwenden, aber ich denke, diese Methode wird derzeit empfohlen, da sie auf die gleiche Weise wie die Validierungsfunktion von Spring MVC überprüft werden kann.
Recommended Posts