GNU/Linux >> Linux Esercitazione >  >> Cent OS

Come implementare la convalida per i servizi RESTful con Spring

Convalida Spring Bean

La convalida dei dati non è un argomento nuovo nello sviluppo di applicazioni web.

Diamo una breve occhiata alla convalida dei dati nell'ecosistema Java in generale e nello Spring Framework in particolare. La piattaforma Java è stata di fatto lo standard per implementare la convalida dei dati questa è la specifica di convalida del bean. La specifica Bean Validation ha diverse versioni:

  • 1.0 (JSR-303),
  • 1.1 (JSR-349),
  • 2.0 (JSR 380) – l'ultima versione

Questa specifica definisce un insieme di componenti, interfacce e annotazioni. Ciò fornisce un modo standard per porre vincoli ai parametri e restituire valori di metodi e parametri di costruttori, fornire API per convalidare oggetti e grafici di oggetti.

Un modello dichiarativo viene utilizzato per inserire vincoli sotto forma di annotazioni sugli oggetti e sui relativi campi. Ci sono annotazioni predefinite come @NotNull , @Digits , @Pattern , @Email , @CreditCard . C'è la possibilità di creare nuovi vincoli personalizzati.

La convalida può essere eseguita manualmente o in modo più naturale, quando altre specifiche e framework convalidano i dati al momento giusto, ad esempio l'input dell'utente, l'inserimento o l'aggiornamento in JPA.

Esempio di convalida in Java

Diamo un'occhiata a come può essere fatto in pratica in questo semplice esempio di Bean Validation all'interno di una normale applicazione Java.

Abbiamo un oggetto che vogliamo convalidare con tutti i campi annotati con vincoli.

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

Ora possiamo usarlo in una semplice applicazione Java e convalidare manualmente l'oggetto.

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() + "]"
      );

    }

  }

}

E il risultato in Console sarà:

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

Vincolo di convalida e annotazione

Crea un vincolo di convalida e un'annotazione personalizzati.


 @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;

  }

}

Nell'esempio sopra, le categorie disponibili provengono da una semplice mappa hash. Nei casi d'uso di applicazioni reali, possono essere recuperati dal database o da qualsiasi altro servizio.

Tieni presente che i vincoli e le convalide possono essere specificati ed eseguiti non solo a livello di campo, ma anche su un intero oggetto.

Quando è necessario convalidare varie dipendenze di campo, ad esempio la data di inizio, non può essere successiva alla data di fine.

Le implementazioni più utilizzate di Bean Validation le specifiche sono Hibernate Validator e Apache BVal .

Convalida con Primavera

Il framework Spring fornisce diverse funzionalità per la convalida.

  • Il supporto per Bean Validation API versioni 1.0, 1.1 (JSR-303, JSR-349) è stato introdotto in Spring Framework a partire dalla versione 3.
  • Spring ha la sua interfaccia Validator che è molto semplice e può essere impostata in un'istanza DataBinder specifica. Questo potrebbe essere utile per implementare la logica di convalida senza annotazioni.

Convalida del bean con Spring

Spring Boot fornisce la convalida avviata che può essere inclusa nel progetto:

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

Questo starter fornisce una versione di Hibernate Validator compatibile con l'attuale Spring Boot.

Utilizzando Bean Validation, potremmo convalidare un corpo di richiesta, parametri di query, variabili all'interno del percorso (ad es. / /simpledto/{id} ), o qualsiasi metodo o parametro del costruttore.

Richieste POST o PUT

Nelle richieste POST o PUT, ad esempio, passiamo il payload JSON, Spring lo converte automaticamente in oggetto Java e ora vogliamo convalidare l'oggetto risultante. Usiamo SimpleDto oggetto dal 1 esempio:

@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;

    }

}

Abbiamo appena aggiunto @Valid annotazione al SimpleDto parametro annotato con @RequestBody . Questo dirà a Spring di elaborare la convalida prima di effettuare una vera e propria chiamata al metodo. Nel caso in cui la convalida fallisca, Spring genererà una MethodArgument NotValidException che, per impostazione predefinita, restituirà una risposta 400 (Richiesta errata).

Convalida delle variabili di percorso

La convalida delle variabili di percorso funziona in modo leggermente diverso. Il problema è che ora dobbiamo aggiungere annotazioni di vincolo direttamente ai parametri del metodo invece che all'interno degli oggetti.

Per farlo funzionare ci sono 2 possibili opzioni:

Opzione 1:@Annotazione convalidata

Aggiungi @Validated annotazione al controller a livello di classe per valutare le annotazioni di vincolo sui parametri del metodo.

Opzione 2:variabile di percorso

Utilizzare un oggetto che rappresenti la variabile di percorso come mostrato nell'esempio seguente:

@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 questo caso, abbiamo SimpleDtoIdParam classe che contiene il simpleDtoId campo che verrà convalidato rispetto a un'annotazione di vincolo Bean standard o personalizzata. Nome della variabile di percorso (/{simpleDtoId} ) deve essere uguale al nome del campo (in modo che Spring possa trovare il setter per questo campo).

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;
    }

}

In contrasto con Organismo di richiesta convalida, Variabile di percorso la convalida genera ConstraintViolationException invece di MethodArgumentNotValidException . Pertanto, dovremo creare un gestore di eccezioni personalizzato.

Il framework Java Spring consente anche di convalidare i parametri a livello di servizio con @Validated annotazione (livello di classe) e @Valid (livello di parametro).

Considerando che Spring JPA sta usando Hibernate sotto, supporta anche la convalida del bean per le classi di entità. Tieni presente che probabilmente non è una buona idea in molti casi fare affidamento su questo livello di convalida poiché significa che tutta la logica prima aveva a che fare con oggetti non validi.

Interfaccia di convalida della primavera

Spring definisce la propria interfaccia per la validazione Validator (org.springframework.validation.Validator). Può essere impostato per un'istanza DataBinder specifica e implementare la convalida senza annotazioni (approccio non dichiarativo).

Per implementare questo approccio avremmo bisogno di:

  1. Implementare l'interfaccia del validatore
  2. Aggiungi validatore

Implementare l'interfaccia di convalida

Implementare l'interfaccia Validator, ad esempio, lavorare con il nostro SimpleDto classe:

@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");

        }

      }

    }

}

Controlla qui se il Datetime creato il timestamp è nel futuro.

Aggiungi validatore

Aggiungi l'implementazione di Validator a DataBinder :

@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;

  }

}

Ora abbiamo SimpleDto convalidato utilizzando le annotazioni di vincolo e la nostra implementazione personalizzata di Spring Validation.


Cent OS
  1. Come gestire i servizi Systemd con Systemctl su Linux

  2. Come scansionare un server Debian per i rootkit con Rkhunter

  3. Come creare un ciclo for con un numero variabile di iterazioni?

  4. Come riscrivere gli URL con mod_rewrite per Apache su Ubuntu 20.04

  5. Come configurare un sottodominio per i servizi di posta elettronica SMTP transazionale

Come integrare Grafana con Prometheus per il monitoraggio

Come eseguire la scansione antivirus con ClamAV su Ubuntu 20.04

Come eseguire i pod come servizi di sistema con Podman

Come configurare Nginx con SSL

Come configurare Redis per un'elevata disponibilità con Sentinel in CentOS 8 – Parte 2

Come automatizzare gli audit di sicurezza Docker con Docker Bench per la sicurezza