java-ee
Java RESTful Web Services (JAX-RS)
Szukaj…
Uwagi
W przeciwieństwie do SOAP i stosu WS, które są określone jako standardy W3C, REST jest naprawdę zbiorem zasad projektowania i korzystania z interfejsu internetowego. Aplikacje REST / RESTful w dużej mierze opierają się na innych standardach:
HTTP
URI, URL
XML, JSON, HTML, GIF, JPEG, and so forth (resource representations)
Rolą JAX-RS (Java API for RESTful Web Services) jest dostarczanie interfejsów API, które wspierają budowanie usług RESTful. JAX-RS to jednak tylko jeden ze sposobów . Usługi RESTful mogą być implementowane na inny sposób w Javie i (rzeczywiście) w wielu innych językach programowania.
Prosty zasób
Przede wszystkim dla aplikacji JAX-RS należy ustawić podstawowy identyfikator URI, z którego będą dostępne wszystkie zasoby. W tym celu klasa javax.ws.rs.core.Application
musi zostać rozszerzona i opatrzona adnotacjami za pomocą adnotacji javax.ws.rs.ApplicationPath
. Adnotacja akceptuje argument łańcuchowy, który definiuje podstawowy identyfikator URI.
@ApplicationPath(JaxRsActivator.ROOT_PATH)
public class JaxRsActivator extends Application {
/**
* JAX-RS root path.
*/
public static final String ROOT_PATH = "/api";
}
Zasoby to proste klasy POJO opatrzone adnotacją @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;
}
}
Gdy żądanie HTTP GET
jest wysyłane do /hello
, zasób odpowiada komunikatem Hello StackOverflow!
wiadomość.
Typy metod 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
bez parametru daje całą zawartość („Witaj świecie!”), A GET
z parametrem ścieżki daje określoną literę z tego ciągu.
Kilka przykładów:
$ curl http://localhost/hello Hello World!
$ curl http://localhost/hello/0 H
$ curl http://localhost/hello/4 o
Uwaga: jeśli @GET
adnotację typu metody (np. @GET
powyżej), domyślną metodą żądania jest metoda obsługi żądań GET.
Metoda 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();
}
}
Spożywać z curl:
$ curl http://localhost/hello
Hello StackOverflow!
$ curl -X "DELETE" http://localhost/hello
$ curl http://localhost/hello
null
Metoda 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();
}
}
Pierwszą metodę można wywołać przez przesłanie formularza HTML, wysyłając przechwycone parametry wejściowe. Formularz przesłania działania powinien wskazywać -
/hello/receiveParams
Druga metoda wymaga komunikatu POJO z modułami pobierającymi / ustawiającymi. Każdy klient REST może wywołać tę metodę z danymi wejściowymi JSON jako -
{"sender":"someone","message":"Hello SO!"}
POJO powinien mieć tę samą właściwość co JSON, aby serializacja działała.
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;
}
}
Maper wyjątków
@Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
@Override
public Response toResponse(IllegalArgumentException exception) {
return Response.serverError().entity("Invalid input: " + exception.getMessage()).build();
}
}
Ten program odwzorowujący wyjątki przechwytuje wszystkie wyjątki IllegalArgumentException zgłoszone w aplikacji i wyświetla użytkownikowi wyraźny komunikat zamiast śledzenia stosu.
UriInfo
Aby uzyskać informacje o identyfikatorze URI, którego agent użytkownika użył do uzyskania dostępu do Twojego zasobu, możesz użyć adnotacji parametru @Context
z parametrem UriInfo
. Obiekt UriInfo
ma kilka metod, za pomocą których można uzyskać różne części identyfikatora 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();
}
}
wyjście GET do 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
Czasami z przyczyn organizacyjnych lub z innych powodów sensowne jest, aby zasób najwyższego poziomu zwrócił podrzędny zasób, który wyglądałby tak. (Twój zasób nie musi być klasą wewnętrzną)
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;
}
}
}
Niestandardowe konwertery parametrów
To jest przykład implementacji konwerterów parametrów niestandardowych dla punktów końcowych JAX-RS. Przykład pokazuje dwie klasy z biblioteki java.time
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;
}
}
Wiązanie nazwy
Wiązanie nazw to koncepcja, która pozwala powiedzieć środowisku wykonawczemu JAX-RS, że określony filtr lub przechwytywacz zostanie wykonany tylko dla określonej metody zasobu. Kiedy filtr lub przechwytywacz jest ograniczony tylko do określonej metody zasobu, mówimy, że jest on związany z nazwą . Filtry i przechwytywacze, które nie mają takiego ograniczenia, nazywane są globalnymi .
Definiowanie adnotacji wiążącej nazwę
Filtry lub przechwytywacze można przypisać do metody zasobu za pomocą adnotacji @NameBinding
. Ta adnotacja jest używana jako adnotacja meta dla innych adnotacji zaimplementowanych przez użytkownika, które są stosowane do dostawców i metod zasobów. Zobacz następujący przykład:
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}
W powyższym przykładzie zdefiniowano nową adnotację @Compress
która jest adnotacją wiążącą nazwę, ponieważ jest opatrzona adnotacją @NameBinding
. Adnotacji @Compress
można użyć do powiązania filtrów i przechwytywacza z punktami końcowymi.
Wiązanie filtra lub przechwytywacza z punktem końcowym
Załóżmy, że masz przechwytywacz, który wykonuje kompresję GZIP i chcesz powiązać taki przechwytywacz z metodą zasobów. Aby to zrobić, należy opisać zarówno metodę zasobu, jak i przechwytywacz w następujący sposób:
@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
jest stosowany w metodzie zasobów getVeryLongString()
i w interceptorze GZIPWriterInterceptor
. Przechwytywacz zostanie wykonany tylko wtedy, gdy zostanie wykonana jakakolwiek metoda zasobu z taką adnotacją.
W powyższym przykładzie przechwytywacz zostanie wykonany tylko dla metody getVeryLongString()
. Przechwytywacz nie zostanie wykonany dla metody getHello()
. W tym przykładzie przyczyna jest prawdopodobnie jasna. Chcemy kompresować tylko długie dane i nie musimy kompresować krótkiej odpowiedzi "Hello World!"
.
Powiązanie nazwy można zastosować w klasie zasobów. W przykładzie HelloWorldResource
będzie opatrzony adnotacją @Compress
. Oznaczałoby to, że wszystkie metody zasobów będą w tym przypadku używać kompresji.
Pamiętaj, że filtry globalne są wykonywane zawsze, więc nawet w przypadku metod zasobów, które mają adnotacje wiążące nazwy.