Поиск…


замечания

В отличие от 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 . Это означает, что все методы ресурсов будут использовать сжатие в этом случае.

Обратите внимание, что глобальные фильтры выполняются всегда, поэтому даже для методов ресурсов, которые имеют любые аннотации привязки имени.

Документация



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow