java-ee
Java RESTful Web Services (JAX-RS)
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.