Suche…


Bemerkungen

Im Gegensatz zu SOAP und dem WS-Stack, die als W3C-Standards spezifiziert sind, ist REST eine Reihe von Prinzipien für das Entwerfen und Verwenden einer webbasierten Schnittstelle. REST / RESTful-Anwendungen hängen stark von anderen Standards ab:

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

Die Funktion von JAX-RS (Java API für RESTful Web Services) besteht darin, APIs bereitzustellen, die die Erstellung von RESTful Services unterstützen. JAX-RS ist jedoch nur eine Möglichkeit, dies zu tun . RESTful-Dienste können auf andere Weise in Java und (in der Tat) in vielen anderen Programmiersprachen implementiert werden.

Einfache Ressource

Zunächst muss für eine JAX-RS-Anwendung ein Basis-URI festgelegt werden, von dem aus alle Ressourcen verfügbar sind. Zu diesem Zweck muss die Klasse javax.ws.rs.core.Application mit der Annotation javax.ws.rs.ApplicationPath erweitert und kommentiert werden. Die Annotation akzeptiert ein String-Argument, das den Basis-URI definiert.

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

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

}

Ressourcen sind einfache POJO- Klassen, die mit der 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;
    }
}

Wenn eine HTTP GET Anforderung an /hello gesendet wird, antwortet die Ressource mit einem Hello StackOverflow! Botschaft.

GET-Methodentypen

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 ohne Parameter gibt den gesamten Inhalt ("Hello World!") Und GET mit Pfadparameter den spezifischen Buchstaben aus dieser Zeichenfolge.

Einige Beispiele:

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

Hinweis: Wenn Sie die Annotation vom Methodentyp @GET (z. B. @GET oben), ist eine Anforderungsmethode standardmäßig ein GET-Anforderungshandler.

DELETE-Methode

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

Konsumiere es mit curl:

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

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


$ curl http://localhost/hello
null

POST-Methode

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

Die erste Methode kann durch Senden eines HTML-Formulars aufgerufen werden, indem erfasste Eingabeparameter gesendet werden. Das Formular zum Senden sollte auf Folgendes zeigen:

/hello/receiveParams

Zweite Methode erfordert Message POJO mit Getter / Setter. Jeder REST-Client kann diese Methode mit JSON-Eingabe als - aufrufen.

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

POJO sollte dieselbe Eigenschaft wie JSON haben, damit die Serialisierung funktioniert.

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

Ausnahme-Mapper

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

Dieser Exception-Mapper fängt alle in der Anwendung ausgelösten IllegalArgumentExceptions ab und zeigt dem Benutzer eine klare Meldung anstelle eines Stacktrace an.

UriInfo

Um Informationen über den URI zu erhalten, den der Benutzeragent für den Zugriff auf Ihre Ressource verwendet hat, können Sie die Annotation UriInfo @Context Parameter mit einem Parameter UriInfo . Das UriInfo Objekt verfügt über einige Methoden, mit denen verschiedene Teile des URI UriInfo werden können.

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

Ausgabe eines GET an 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

SubResources

Aus organisatorischen oder anderen Gründen ist es manchmal sinnvoll, wenn Ihre oberste Ressource eine untergeordnete Ressource zurückgibt, die so aussieht. (Ihre Subressource muss keine innere Klasse sein.)

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

Benutzerdefinierte Parameterkonverter

Dies ist ein Beispiel für die Implementierung benutzerdefinierter Parameterkonverter für JAX-RS-Endpunkte. Das Beispiel zeigt zwei Klassen aus der Java- java.time Bibliothek von 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;
  }
}

Namensbindung

Namensbindung ist ein Konzept, mit dem einer JAX-RS-Laufzeitumgebung mitgeteilt werden kann, dass ein bestimmter Filter oder ein bestimmter Interceptor nur für eine bestimmte Ressourcenmethode ausgeführt wird. Wenn ein Filter oder ein Interceptor nur auf eine bestimmte Ressourcenmethode beschränkt ist, sagen wir, dass er an den Namen gebunden ist . Filter und Interzeptoren, für die keine solche Einschränkung gilt, werden als global bezeichnet .

Definition einer Namensbindung

Filter oder Interceptors können mithilfe der Annotation @NameBinding einer Ressourcenmethode @NameBinding . Diese Annotation wird als Meta-Annotation für andere vom Benutzer implementierte Annotationen verwendet, die auf Anbieter und Ressourcenmethoden angewendet werden. Siehe folgendes Beispiel:

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

Das obige Beispiel definiert eine neue Annotation @Compress der es sich um eine @Compress handelt, da diese mit @NameBinding kommentiert @NameBinding . Mit der @Compress Annotation können Filter und Interceptor an Endpunkte @Compress werden.

Binden eines Filters oder Interceptors an einen Endpunkt

Stellen Sie sich vor, Sie haben einen Interceptor, der eine GZIP-Komprimierung durchführt, und Sie möchten diesen Interceptor an eine Ressourcenmethode binden. Dazu kommentieren Sie sowohl die Ressourcenmethode als auch den Interceptor wie folgt:

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

@Compress wird auf die Ressourcenmethode getVeryLongString() und auf den Interceptor GZIPWriterInterceptor . Der Interceptor wird nur ausgeführt, wenn eine Ressourcenmethode mit einer solchen Annotation ausgeführt wird.

Im obigen Beispiel wird der Interceptor nur für die Methode getVeryLongString() . Der Interceptor wird für die Methode getHello() nicht ausgeführt. In diesem Beispiel ist der Grund wahrscheinlich klar. Wir möchten nur lange Daten komprimieren und müssen die kurze Antwort von "Hello World!" Nicht komprimieren. .

Namensbindung kann auf eine Ressourcenklasse angewendet werden. Im Beispiel HelloWorldResource würde mit Anmerkungen versehen werden @Compress . Dies würde bedeuten, dass alle Ressourcenmethoden in diesem Fall eine Komprimierung verwenden.

Beachten Sie, dass globale Filter immer ausgeführt werden. Dies gilt auch für Ressourcenmethoden, die mit Anmerkungen zur Namensbindung versehen sind.

Dokumentation



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow