Buscar..


Observaciones

A diferencia de SOAP y la pila WS, que se especifican como estándares W3C, REST es realmente un conjunto de principios para diseñar y usar una interfaz basada en web. Las aplicaciones REST / RESTful dependen en gran medida de otras normas:

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

La función de JAX-RS (API de Java para servicios web RESTful) es proporcionar API que admitan la creación de servicios RESTful. Sin embargo, JAX-RS es solo una forma de hacer esto . Los servicios RESTful pueden implementarse de otras formas en Java y (de hecho) en muchos otros lenguajes de programación.

Recurso simple

En primer lugar, para una aplicación JAX-RS debe establecerse un URI base desde el cual todos los recursos estarán disponibles. Para ese propósito, la clase javax.ws.rs.core.Application debe extenderse y anotarse con la anotación javax.ws.rs.ApplicationPath . La anotación acepta un argumento de cadena que define el URI base.

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

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

}

Los recursos son clases de POJO simples que se anotan con la anotación @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;
    }
}

Cuando se envía una solicitud HTTP GET a /hello , el recurso responde con un Hello StackOverflow! mensaje.

Tipos de métodos 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 sin un parámetro proporciona todo el contenido ("Hello World!") Y GET con el parámetro de ruta proporciona la letra específica de esa cadena.

Algunos ejemplos:

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

Nota: si @GET la anotación de tipo de método (por ejemplo, la @GET anterior), un método de solicitud de forma predeterminada es un controlador de solicitud GET.

Método de eliminación

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

Consumirlo con rizo:

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

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


$ curl http://localhost/hello
null

Método 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();
    }
}

El primer método puede invocarse a través del envío de formularios HTML mediante el envío de parámetros de entrada capturados. La acción de envío de formulario debe apuntar a:

/hello/receiveParams

El segundo método requiere el mensaje POJO con captadores / configuradores. Cualquier cliente REST puede llamar a este método con entrada JSON como:

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

POJO debe tener la misma propiedad que JSON para hacer que la serialización funcione.

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

Asignador de excepciones

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

Este mapeador de excepciones capturará todas las IllegalArgumentExceptions lanzadas en la aplicación y le mostrará al usuario un mensaje claro en lugar de un seguimiento de pila.

UriInfo

Para obtener información sobre el URI que el agente de usuario utilizó para acceder a su recurso, puede usar la anotación del parámetro @Context con un parámetro UriInfo . El objeto UriInfo tiene algunos métodos que pueden usarse para obtener diferentes partes de la 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();
    }
}

salida de un GET a 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

SubRecursos

A veces, por razones organizativas u otras, tiene sentido que su recurso de nivel superior devuelva un sub-recurso que se vea así. (Su sub-recurso no necesita ser una clase interna)

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

Convertidores de parametros personalizados

Este es un ejemplo de cómo implementar convertidores de parámetros personalizados para puntos finales JAX-RS. El ejemplo muestra dos clases de la biblioteca 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;
  }
}

Enlace de nombre

El enlace de nombres es un concepto que permite decir a un tiempo de ejecución de JAX-RS que un filtro o interceptor específico se ejecutará solo para un método de recurso específico. Cuando un filtro o un interceptor se limita solo a un método de recurso específico, decimos que está vinculado a un nombre . Los filtros e interceptores que no tienen dicha limitación se denominan globales .

Definición de una anotación de enlace de nombre

Los filtros o interceptores pueden asignarse a un método de recurso utilizando la anotación @NameBinding . Esta anotación se utiliza como meta anotación para otras anotaciones implementadas por el usuario que se aplican a proveedores y métodos de recursos. Vea el siguiente ejemplo:

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

El ejemplo anterior define una nueva anotación de @Compress que es una anotación de enlace de nombre como se anota con @NameBinding . La anotación @Compress se puede utilizar para enlazar filtros e interceptor a puntos finales.

Unir un filtro o un interceptor a un punto final

Considere que tiene un interceptor que realiza la compresión GZIP y desea vincular dicho interceptor a un método de recursos. Para hacerlo, anote tanto el método de recursos como el interceptor, de la siguiente manera:

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

El @Compress se aplica en el método de recurso getVeryLongString() y en el interceptor GZIPWriterInterceptor . El interceptor se ejecutará solo si se ejecutará algún método de recurso con dicha anotación.

En el ejemplo anterior, el interceptor se ejecutará solo para el método getVeryLongString() . El interceptor no se ejecutará para el método getHello() . En este ejemplo, la razón es probablemente clara. Nos gustaría comprimir solo datos largos y no necesitamos comprimir la respuesta corta de "Hello World!" .

El enlace de nombres se puede aplicar a una clase de recurso. En el ejemplo, HelloWorldResource se @Compress con @Compress . Esto significaría que todos los métodos de recursos usarán la compresión en este caso.

Tenga en cuenta que los filtros globales se ejecutan siempre, incluso para los métodos de recursos que tienen anotaciones de enlace de nombre.

Documentación



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow