java-ee
Java RESTful Web Services (JAX-RS)
Поиск…
замечания
В отличие от SOAP и WS-стека, которые определены как стандарты W3C, REST - это действительно набор принципов для проектирования и использования веб-интерфейса. Приложения REST / RESTful в значительной степени зависят от других стандартов:
HTTP
URI, URL
XML, JSON, HTML, GIF, JPEG, and so forth (resource representations)
Роль JAX-RS (Java API для веб-служб RESTful) заключается в предоставлении API-интерфейсов, которые поддерживают создание служб RESTful. Однако JAX-RS - это всего лишь один из способов сделать это . RESTful-сервисы могут быть реализованы другими способами на Java и (действительно) на многих других языках программирования.
Простой ресурс
Прежде всего для приложения JAX-RS должен быть установлен базовый URI, из которого будут доступны все ресурсы. Для этого класс javax.ws.rs.core.Application
должен быть расширен и аннотирован аннотацией javax.ws.rs.ApplicationPath
. Аннотации принимают строковый аргумент, который определяет базовый URI.
@ApplicationPath(JaxRsActivator.ROOT_PATH)
public class JaxRsActivator extends Application {
/**
* JAX-RS root path.
*/
public static final String ROOT_PATH = "/api";
}
Ресурсы - это простые классы POJO, которые аннотируются аннотацией @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;
}
}
Когда HTTP GET
отправляется в /hello
, ресурс отвечает Hello StackOverflow!
сообщение.
Типы методов 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
без параметра дает весь контент («Hello World!») И GET
с параметром path дает конкретную букву из этой строки.
Некоторые примеры:
$ curl http://localhost/hello Hello World!
$ curl http://localhost/hello/0 H
$ curl http://localhost/hello/4 o
Примечание: если вы не @GET
аннотацию типа метода (например, @GET
выше), метод запроса по умолчанию является обработчиком запроса GET.
Метод 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();
}
}
Потребляйте его с помощью завитка:
$ curl http://localhost/hello
Hello StackOverflow!
$ curl -X "DELETE" http://localhost/hello
$ curl http://localhost/hello
null
Метод 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();
}
}
Первый метод может быть вызван посредством отправки формы HTML, отправив захваченные входные параметры. Форма подачи действия должна указывать на -
/hello/receiveParams
Второй метод требует Message POJO с геттерами / сеттерами. Любой клиент REST может вызывать этот метод с вводом JSON as -
{"sender":"someone","message":"Hello SO!"}
POJO должен иметь то же свойство, что и JSON, чтобы выполнить сериализацию.
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;
}
}
Экземпляр исключения
@Provider
public class IllegalArgumentExceptionMapper implements ExceptionMapper<IllegalArgumentException> {
@Override
public Response toResponse(IllegalArgumentException exception) {
return Response.serverError().entity("Invalid input: " + exception.getMessage()).build();
}
}
Этот обработчик исключений поймает все вызовы IllegalArgumentExceptions, созданные в приложении, и покажет пользователю четкое сообщение вместо stacktrace.
UriInfo
Чтобы получить информацию об URI, которую пользовательский агент использовал для доступа к вашему ресурсу, вы можете использовать @Context
параметра @Context
с параметром UriInfo
. Объект UriInfo
имеет несколько методов, которые можно использовать для получения разных частей 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();
}
}
вывод GET в 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
Иногда по организационным или иным причинам имеет смысл вернуть ресурс верхнего уровня суб-ресурсу, который будет выглядеть следующим образом. (Ваш субресурс не должен быть внутренним классом)
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;
}
}
}
Преобразователи настраиваемых параметров
Это пример того, как реализовать специализированные преобразователи параметров для конечных точек JAX-RS. В этом примере показаны два класса из библиотеки 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;
}
}
Связывание имени
Связывание имен - это концепция, которая позволяет говорить во время выполнения JAX-RS, что конкретный фильтр или перехватчик будет выполняться только для определенного метода ресурсов. Когда фильтр или перехватчик ограничивается только определенным методом ресурсов, мы говорим, что он связан с именем . Фильтры и перехватчики, которые не имеют такого ограничения, называются глобальными .
Определение аннотации привязки имени
Фильтры или перехватчики могут быть назначены методу ресурса с использованием аннотации @NameBinding
. Эта аннотация используется как мета-аннотация для других пользовательских аннотаций, которые применяются к поставщикам и ресурсным методам. См. Следующий пример:
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}
В приведенном выше примере определяется новая аннотация @Compress
которая является аннотацией привязки имени, поскольку она аннотируется с @NameBinding
. Аннотацию @Compress
можно использовать для привязки фильтров и перехватчика к конечным точкам.
Связывание фильтра или перехватчика с конечной точкой
У вас есть перехватчик, который выполняет сжатие GZIP, и вы хотите связать такой перехватчик с методом ресурсов. Чтобы сделать это, аннотируйте как ресурсный метод, так и перехватчик, как показано ниже:
@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
применяется к методу ресурса getVeryLongString()
и к перехватчику GZIPWriterInterceptor
. Перехватчик будет выполняться только в том случае, если будет выполнен какой-либо метод ресурсов с такой аннотацией.
В приведенном выше примере перехватчик будет выполняться только для getVeryLongString()
. Перехватчик не будет выполнен для метода getHello()
. В этом примере причина, вероятно, очевидна. Мы хотели бы сжать только длинные данные, и нам не нужно сжимать короткий ответ "Hello World!"
,
Связывание имен может применяться к классу ресурсов. В примере HelloWorldResource
будет аннотирован с помощью @Compress
. Это означает, что все методы ресурсов будут использовать сжатие в этом случае.
Обратите внимание, что глобальные фильтры выполняются всегда, поэтому даже для методов ресурсов, которые имеют любые аннотации привязки имени.