Convalida dei fagioli in Jersey

1. Panoramica

In questo tutorial, daremo uno sguardo alla convalida dei bean utilizzando il framework open source Jersey.

Come abbiamo già visto negli articoli precedenti, Jersey è un framework open source per lo sviluppo di servizi Web RESTful. Possiamo ottenere maggiori dettagli su Jersey nella nostra introduzione su come creare un'API con Jersey e Spring.

2. Bean Validation in Jersey

La convalida è il processo di verifica che alcuni dati obbediscano a uno o più vincoli predefiniti . È, ovviamente, un caso d'uso molto comune nella maggior parte delle applicazioni.

Il framework Java Bean Validation (JSR-380) è diventato lo standard di fatto per la gestione di questo tipo di operazioni in Java. Per ricapitolare le basi della convalida di Java Bean, fare riferimento al nostro tutorial precedente.

Jersey contiene un modulo di estensione per supportare Bean Validation . Per utilizzare questa funzionalità nella nostra applicazione, dobbiamo prima configurarla. Nella prossima sezione vedremo come configurare la nostra applicazione.

3. Configurazione dell'applicazione

Ora, partiamo dal semplice esempio dell'API Fruit dall'eccellente articolo di supporto Jersey MVC.

3.1. Dipendenze di Maven

Prima di tutto, aggiungiamo la dipendenza Bean Validation al nostro pom.xml :

 org.glassfish.jersey.ext jersey-bean-validation 2.27 

Possiamo ottenere l'ultima versione da Maven Central.

3.2. Configurazione del server

In Jersey, normalmente registriamo la funzione di estensione che vogliamo utilizzare nella nostra classe di configurazione delle risorse personalizzate.

Tuttavia, per l'estensione di convalida del bean, non è necessario eseguire questa registrazione. Fortunatamente, questa è una delle poche estensioni che il framework Jersey registra automaticamente.

Infine, per inviare errori di convalida al client , aggiungeremo una proprietà del server alla nostra configurazione personalizzata delle risorse :

public ViewApplicationConfig() { packages("com.baeldung.jersey.server"); property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true); } 

4. Convalida dei metodi di risorsa JAX-RS

In questa sezione, spiegheremo due diversi modi per convalidare i parametri di input utilizzando le annotazioni dei vincoli:

  • Utilizzo dei vincoli dell'API di convalida dei bean incorporati
  • Creazione di un vincolo e di un validatore personalizzati

4.1. Utilizzo delle annotazioni dei vincoli incorporate

Iniziamo esaminando le annotazioni dei vincoli incorporate:

@POST @Path("/create") @Consumes(MediaType.APPLICATION_FORM_URLENCODED) public void createFruit( @NotNull(message = "Fruit name must not be null") @FormParam("name") String name, @NotNull(message = "Fruit colour must not be null") @FormParam("colour") String colour) { Fruit fruit = new Fruit(name, colour); SimpleStorageService.storeFruit(fruit); } 

In questo esempio, creiamo un nuovo Fruit utilizzando due parametri del modulo, nome e colore . Usiamo l' annotazione @NotNull che fa già parte dell'API Bean Validation.

Questo impone un semplice vincolo non nullo sui nostri parametri del form. Nel caso in cui uno dei parametri sia nullo , verrà restituito il messaggio dichiarato all'interno dell'annotazione .

Naturalmente, lo dimostreremo con uno unit test:

@Test public void givenCreateFruit_whenFormContainsNullParam_thenResponseCodeIsBadRequest() { Form form = new Form(); form.param("name", "apple"); form.param("colour", null); Response response = target("fruit/create").request(MediaType.APPLICATION_FORM_URLENCODED) .post(Entity.form(form)); assertEquals("Http Response should be 400 ", 400, response.getStatus()); assertThat(response.readEntity(String.class), containsString("Fruit colour must not be null")); } 

Nell'esempio sopra, usiamo la classe di supporto JerseyTest per testare la nostra risorsa frutta . Inviamo una richiesta POST con un colore nullo e controlliamo che la risposta contenga il messaggio atteso.

Per un elenco dei vincoli di convalida incorporati, dai un'occhiata alla documentazione.

4.2. Definizione di un'annotazione di vincolo personalizzata

A volte dobbiamo imporre vincoli più complessi. Possiamo farlo definendo la nostra annotazione personalizzata.

Usando il nostro semplice esempio di Fruit API, immaginiamo di dover convalidare che tutti i frutti abbiano un numero di serie valido:

@PUT @Path("/update") @Consumes("application/x-www-form-urlencoded") public void updateFruit(@SerialNumber @FormParam("serial") String serial) { //... } 

In questo esempio, il parametro serial deve soddisfare i vincoli definiti da @SerialNumber , che definiremo in seguito.

Definiremo prima l'annotazione del vincolo:

@Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = { SerialNumber.Validator.class }) public @interface SerialNumber { String message() default "Fruit serial number is not valid"; Class[] groups() default {}; Class[] payload() default {}; } 

Successivamente, definiremo la classe del validatore SerialNumber.Validator :

public class Validator implements ConstraintValidator { @Override public void initialize(SerialNumber serial) { } @Override public boolean isValid(String serial, ConstraintValidatorContext constraintValidatorContext) { String serialNumRegex = "^\\d{3}-\\d{3}-\\d{4}$"; return Pattern.matches(serialNumRegex, serial); } } 

Il punto chiave qui è che la classe Validator deve implementare ConstraintValidator dove T è il tipo di valore che vogliamo convalidare, nel nostro caso una String .

Infine, implementiamo la nostra logica di convalida personalizzata nel metodo isValid .

5. Convalida delle risorse

Inoltre, l'API Bean Validation ci consente anche di convalidare gli oggetti utilizzando l' annotazione @Valid .

Nella sezione successiva, spiegheremo due diversi modi per convalidare le classi di risorse utilizzando questa annotazione:

  • Innanzitutto, richiedi la convalida delle risorse
  • In secondo luogo, la convalida delle risorse di risposta

Cominciamo aggiungendo l' annotazione @Min al nostro oggetto Fruit :

@XmlRootElement public class Fruit { @Min(value = 10, message = "Fruit weight must be 10 or greater") private Integer weight; //... } 

5.1. Richiedi la convalida delle risorse

Prima di tutto, abiliteremo la convalida utilizzando @Valid nella nostra classe FruitResource :

@POST @Path("/create") @Consumes("application/json") public void createFruit(@Valid Fruit fruit) { SimpleStorageService.storeFruit(fruit); } 

Nell'esempio sopra, se proviamo a creare un frutto con un peso inferiore a 10 otterremo un errore di convalida.

5.2. Convalida delle risorse di risposta

Allo stesso modo, nel prossimo esempio, vedremo come convalidare una risorsa di risposta:

@GET @Valid @Produces("application/json") @Path("/search/{name}") public Fruit findFruitByName(@PathParam("name") String name) { return SimpleStorageService.findByName(name); }

Nota come usiamo la stessa annotazione @Valid . Ma questa volta lo usiamo a livello di metodo delle risorse per essere sicuri che la risposta sia valida.

6. Gestore di eccezioni personalizzato

In quest'ultima parte, vedremo brevemente come creare un gestore di eccezioni personalizzato. Ciò è utile quando vogliamo restituire una risposta personalizzata se violiamo un particolare vincolo.

Let's begin by defining our FruitExceptionMapper:

public class FruitExceptionMapper implements ExceptionMapper { @Override public Response toResponse(ConstraintViolationException exception) { return Response.status(Response.Status.BAD_REQUEST) .entity(prepareMessage(exception)) .type("text/plain") .build(); } private String prepareMessage(ConstraintViolationException exception) { StringBuilder message = new StringBuilder(); for (ConstraintViolation cv : exception.getConstraintViolations()) { message.append(cv.getPropertyPath() + " " + cv.getMessage() + "\n"); } return message.toString(); } }

First of all, we define a custom exception mapping provider. In order to do this, we implement the ExceptionMapper interface using a ConstraintViolationException.

Hence, we'll see that when this exception is thrown the toResponse method of our custom exception mapper instance will be invoked .

Also, in this simple example, we iterate through all the violations and append each property and message to be sent back in the response.

Next, in order to use our custom exception mapper we need to register our provider:

@Override protected Application configure() { ViewApplicationConfig config = new ViewApplicationConfig(); config.register(FruitExceptionMapper.class); return config; }

Finally, we add an endpoint to return an invalid Fruit to show the exception handler in action:

@GET @Produces(MediaType.TEXT_HTML) @Path("/exception") @Valid public Fruit exception() { Fruit fruit = new Fruit(); fruit.setName("a"); fruit.setColour("b"); return fruit; } 

7. Conclusion

Per riassumere, in questo tutorial, abbiamo esplorato l'estensione API Jersey Bean Validation.

Innanzitutto, abbiamo iniziato introducendo come è possibile utilizzare l'API di convalida dei bean in Jersey. Inoltre, abbiamo esaminato come configurare un'applicazione web di esempio.

Infine, abbiamo esaminato diversi modi per eseguire la convalida con Jersey e come scrivere un gestore di eccezioni personalizzato.

Come sempre, il codice sorgente completo dell'articolo è disponibile su GitHub.