Recherche…


Remarques

Contrairement à SOAP et à WS-stack, qui sont spécifiés comme normes W3C, REST est en réalité un ensemble de principes pour la conception et l’utilisation d’une interface Web. Les applications REST / RESTful reposent largement sur d’autres normes:

HTTP
URI, URL
XML, JSON, HTML, GIF, JPEG, and so forth (resource representations)

Le rôle de JAX-RS (API Java pour RESTful Web Services) est de fournir des API prenant en charge la création de services RESTful. Cependant, JAX-RS n'est qu'un moyen d'y parvenir . Les services RESTful peuvent être implémentés d'autres manières en Java et (en effet) dans de nombreux autres langages de programmation.

Ressource simple

Tout d'abord, pour une application JAX-RS, vous devez définir un URI de base à partir duquel toutes les ressources seront disponibles. Pour cela, la classe javax.ws.rs.core.Application doit être étendue et annotée à l' javax.ws.rs.ApplicationPath annotation javax.ws.rs.ApplicationPath . L'annotation accepte un argument de chaîne qui définit l'URI de base.

@ApplicationPath(JaxRsActivator.ROOT_PATH)
public class JaxRsActivator extends Application {

    /**
     * JAX-RS root path.
     */
    public static final String ROOT_PATH = "/api";

}

Les ressources sont de simples classes POJO qui sont annotées avec l'annotation @Path .

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/hello")
public class HelloWorldResource {
    public static final String MESSAGE = "Hello StackOverflow!";

    @GET
    @Produces("text/plain")
    public String getHello() {
        return MESSAGE;
    }
}

Lorsqu'une HTTP GET est envoyée à /hello , la ressource répond avec un Hello StackOverflow! message.

Types de méthode GET

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("/hello")
public class HelloWorldResource {
    public static final String MESSAGE = "Hello World!";

    @GET
    @Produces("text/plain")
    public String getHello() {
        return MESSAGE;
    }

    @GET
    @Path("/{letter}")
    @Produces("text/plain")
    public String getHelloLetter(@PathParam("letter") int letter){
        if (letter >= 0 && letter < MESSAGE.length()) {
            return MESSAGE.substring(letter, letter + 1);
        } else {
            return "";
        }
    }
}

GET sans paramètre donne tout le contenu ("Hello World!") Et le paramètre GET with path donne la lettre spécifique de cette chaîne.

Quelques exemples:

$ curl http://localhost/hello
Hello World!
$ curl http://localhost/hello/0
H
$ curl http://localhost/hello/4
o

Remarque: si vous @GET l'annotation de type de méthode (par exemple, @GET ci-dessus), une méthode de requête est par défaut un gestionnaire de requête GET.

Méthode DELETE

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Response;

@Path("hello")
public class HelloWorldResource {

    private String message = "Hello StackOverflow!";

    @GET
    @Produces("text/plain")
    public String getHello() {
        return message;
    }

    @DELETE
    public Response deleteMessage() {
        message = null;
        return Response.noContent().build();
    }
}

Consommez-le avec une boucle:

$ curl http://localhost/hello
Hello StackOverflow!

$ curl -X "DELETE" http://localhost/hello


$ curl http://localhost/hello
null

Méthode POST

import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("hello")
public class HelloWorldResource {
    @POST
    @Path("/receiveParams")
    public Response receiveHello(@FormParam("name") String name, @FormParam("message") String message) {
        //process parameters
        return Response.status(200).build();
    }

    @POST
    @Path("/saveObject")
    @Consumes("application/json")
    public Response saveMessage(Message message) {
        //process message json
        return Response.status(200).entity("OK").build();
    }
}

La première méthode peut être invoquée via l'envoi de formulaires HTML en envoyant des paramètres d'entrée capturés. L'action de soumission de formulaire doit indiquer -

/hello/receiveParams

La seconde méthode nécessite un message POJO avec des getters / setters. Tout client REST peut appeler cette méthode avec une entrée JSON comme -

{"sender":"someone","message":"Hello SO!"}

POJO doit avoir la même propriété que JSON pour que la sérialisation fonctionne.

public class Message {

    String sender;
    String message;

    public String getSender() {
        return sender;
    }
    public void setSender(String sender) {
        this.sender = sender;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

Mappeur d'exception

@Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
        
  @Override
  public Response toResponse(IllegalArgumentException exception) {
    return Response.serverError().entity("Invalid input: " + exception.getMessage()).build();
  }
}

Ce mappeur d'exception intercepte toutes les exceptions IllegalArgumentException lancées dans l'application et affiche à l'utilisateur un message clair au lieu d'une trace de pile.

UriInfo

Afin d'obtenir des informations sur l'URI utilisé par l'agent utilisateur pour accéder à votre ressource, vous pouvez utiliser l'annotation du paramètre @Context avec un paramètre UriInfo . L'objet UriInfo a quelques méthodes qui peuvent être utilisées pour obtenir différentes parties de l'URI.

//server is running on https://localhost:8080,
// webapp is at /webapp, servlet at /webapp/servlet
@Path("class")
class Foo {
    
    @GET
    @Path("resource")
    @Produces(MediaType.TEXT_PLAIN)
    public Response getResource(@Context UriInfo uriInfo) {
        StringBuilder sb = new StringBuilder();
        sb.append("Path: " + uriInfo.getPath() + "\n");
        sb.append("Absolute Path: " + uriInfo.getAbsolutePath() + "\n");
        sb.append("Base URI: " + uriInfo.getBaseUri() + "\n");
        sb.append("Request URI: " + uriInfo.getRequestUri() + "\n");
        return Response.ok(sb.toString()).build();
    }
}

sortie d'un GET sur https://localhost:8080/webapp/servlet/class/resource :

Path: class/resource
Absolute Path: https://localhost:8080/webapp/servlet/class/resource#
Base URI: https://localhost:8080/webapp/servlet/
Request URI: https://localhost:8080/webapp/servlet/class/resource

Les sous-ressources

Parfois, pour des raisons organisationnelles ou autres, il est logique que votre ressource de niveau supérieur renvoie une sous-ressource ressemblant à ceci. (Votre sous-ressource n'a pas besoin d'être une classe interne)

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;

@Path("items")
public class ItemsResource {

    @Path("{id}")
    public String item(@PathParam("id") String id) {
        return new ItemSubResource(id);
    }

    public static class ItemSubResource {

        private final String id;

        public ItemSubResource(String id) {
            this.id = id;
        }
        
        @GET
        @Produces("text/plain")
        public Item item() {
            return "The item " + id;
        }
    }
}

Convertisseurs de paramètres personnalisés

Voici un exemple d'implémentation de convertisseurs de paramètres personnalisés pour les noeuds finaux JAX-RS. L'exemple montre deux classes de la bibliothèque java.time de Java 8.

@Provider
public class ParamConverters implements ParamConverterProvider {  
  @Override
  public <T> ParamConverter<T> getConverter(Class<T> rawType,
                                            Type genericType,
                                            Annotation[] annotations)
  {
    if (rawType == LocalDate.class)
      return (ParamConverter<T>) new ParamConverter<LocalDate>() {
        @Override
        public LocalDate fromString(String value) {
          return LocalDate.parse(value);
        }

        @Override
        public String toString(LocalDate value) {
          return null;
        }
      };
    else if (rawType == MonthDay.class)
      return (ParamConverter<T>) new ParamConverter<MonthDay>() {
        @Override
        public MonthDay fromString(String value) {
          int[] ddmm = Arrays.stream(value.split("/"))
                             .mapToInt(Integer::parseInt)
                             .toArray();
          return MonthDay.of(ddmm[1], ddmm[0]);
        }

        @Override
        public String toString(MonthDay value) {
          return null;
        }
      };
    return null;
  }
}

Liaison de nom

La liaison de noms est un concept qui permet de dire à un runtime JAX-RS qu'un filtre ou un intercepteur spécifique ne sera exécuté que pour une méthode de ressource spécifique. Lorsqu'un filtre ou un intercepteur est limité à une méthode de ressource spécifique, nous disons qu'il est lié à un nom . Les filtres et intercepteurs sans limitation sont appelés globaux .

Définition d'une annotation de liaison de nom

Les filtres ou les intercepteurs peuvent être affectés à une méthode de ressource à l'aide de l'annotation @NameBinding . Cette annotation est utilisée comme annotation de méta pour les autres annotations implémentées par l'utilisateur qui sont appliquées à un fournisseur et à des méthodes de ressource. Voir l'exemple suivant:

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}

L'exemple ci-dessus définit une nouvelle annotation @Compress qui est une annotation de liaison de nom telle qu'elle est annotée avec @NameBinding . L'annotation @Compress peut être utilisée pour lier des filtres et des intercepteurs aux points d'extrémité.

Relier un filtre ou un intercepteur à un noeud final

Considérez que vous avez un intercepteur qui effectue la compression GZIP et que vous souhaitez lier un tel intercepteur à une méthode de ressource. Pour ce faire, annotez la méthode de ressource et l'intercepteur comme suit:

@Compress
public class GZIPWriterInterceptor implements WriterInterceptor {

    @Override
    public void aroundWriteTo(WriterInterceptorContext context)
                    throws IOException, WebApplicationException {
        final OutputStream outputStream = context.getOutputStream();
        context.setOutputStream(new GZIPOutputStream(outputStream));
        context.proceed();
    }
}
@Path("helloworld")
public class HelloWorldResource {
 
    @GET
    @Produces("text/plain")
    public String getHello() {
        return "Hello World!";
    }
 
    @GET
    @Path("too-much-data")
    @Compress
    public String getVeryLongString() {
        String str = ... // very long string
        return str;
    }
}

Le @Compress est appliqué à la méthode de ressource getVeryLongString() et à l'intercepteur GZIPWriterInterceptor . L'intercepteur sera exécuté uniquement si une méthode de ressource avec une telle annotation sera exécutée.

Dans l'exemple ci-dessus, l'intercepteur sera exécuté uniquement pour la méthode getVeryLongString() . L'intercepteur ne sera pas exécuté pour la méthode getHello() . Dans cet exemple, la raison est probablement claire. Nous aimerions compresser uniquement les données longues et nous n'avons pas besoin de compresser la réponse courte de "Hello World!" .

La liaison de noms peut être appliquée à une classe de ressources. Dans l'exemple, HelloWorldResource serait annoté avec @Compress . Cela signifie que toutes les méthodes de ressources utiliseront la compression dans ce cas.

Notez que les filtres globaux sont exécutés toujours, même pour les méthodes de ressources qui ont des annotations de liaison de noms.

Documentation



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow