Вопрос:

Установите заголовки ответа JAX-RS в реализации, не выставляя HttpServletResponse в интерфейсе

java rest jax-rs httpresponse

6104 просмотра

3 ответа

6833 Репутация автора

У меня есть серверная реализация RESTful, а также библиотека для клиентов, чтобы делать звонки, все с использованием JAX-RS. Компоненты сервера делятся на интерфейс FooResourceи реализацию FooResourceService.

Чтобы клиентские и серверные библиотеки могли совместно использовать путь RESTful и другие определения, я хотел разделить FooResourceинтерфейс на собственный проект:

@Path(value = "foo")
public interface FooResource {

  @GET
  public Bar getBar(@PathParam(value = "{id}") int id) {

Я хочу установить некоторые заголовки в ответе. Один из простых способов сделать это - использовать @Context HttpServletResponseв сигнатуре метода:

  public Bar getBar(@PathParam(value = "{id}") int id, @Context HttpServletResponse servletResponse) {

Но проблема в том, что это раскрывает детали реализации в интерфейсе. Более конкретно, он внезапно требует, чтобы мой проект определения REST (который совместно используется клиентской и серверной библиотекой) извлекал javax.servlet-apiзависимость - то, что клиенту не нужно (или не нужно).

Как моя реализация службы ресурсов RESTful может устанавливать заголовки HTTP-ответов, не используя эту зависимость в интерфейсе ресурса?

Я видел один пост, рекомендующий ввести HttpServletResponse как член класса. Но как это будет работать, если моя реализация службы ресурсов будет одноэлементной? Использует ли он какой-то прокси с локальными потоками или что-то, что вычисляет правильный ответ сервлета, даже если одноэлементный класс используется одновременно несколькими потоками? Есть ли другие решения?

Автор: Garret Wilson Источник Размещён: 25.12.2014 01:33

Ответы (3)


-2 плюса

3307 Репутация автора

@Path("/foo")
public interface FooResource {

    @GET
    @Path("{id}")
    public Response getBar(@PathParam("id") int id) {
        Bar bar = new Bar();
        //Do some logic on bar
        return Response.ok().entity(bar).header("header-name", "header-value").build()
    }
}

Возвращает JSON-представление экземпляра barс кодом состояния 200 и заголовка header-nameсо значением header-value. Это должно выглядеть примерно так:

{
    "bar-field": "bar-field-value",
    "bar-field-2": "bar-field-2"
}
Автор: Edd Размещён: 25.12.2014 02:10

2 плюса

164353 Репутация автора

Таким образом, инъекция HttpServletResponseкажется не ходом. Только определенные прокси-типы могут вводиться в синглтоны. Я считаю, что полный список выглядит следующим образом:

HttpHeaders, Request, UriInfo, SecurityContext

Это несколько указано в спецификации JAX-RS, но более четко объясняется в справочном руководстве Джерси

Исключение существует для определенных объектов запроса, которые могут вводиться даже в поля конструктора или класса. Для этих объектов среда выполнения будет внедрять прокси, которые могут одновременно обрабатывать больше запросов. Эти объекты запроса являются HttpHeaders, Request, UriInfo, SecurityContext. Эти прокси могут быть введены с помощью @Contextаннотации.

SecurityContext может быть, Джерси, как это не указано в спецификации, но я не уверен.

Теперь эти типы, упомянутые выше, на самом деле мало что делают для вас, потому что все они являются контекстами запроса и ничего не могут установить ответ.

Однако одна идея состоит в том, чтобы использовать javax.ws.rs.container.ContainerResponseFilter, наряду с, HttpHeadersдля установки временного заголовка запроса. Вы можете получить доступ к этому заголовку через ContainerRequestContextпереданный filterметоду. Затем просто установите заголовок ответа через ContainerResponseContext, также передаваемый filterметоду. Если заголовок не является специфическим для контекста этого метода ресурса, то это даже проще. Просто установите заголовок в фильтре.

Но допустим, что заголовок зависит от выполнения метода ресурса. Тогда вы могли бы сделать что-то вроде

@Singleton
@Path("/singleton")
public class SingletonResource {

    @Context
    javax.ws.rs.core.HttpHeaders headers;

    @GET
    public String getHello() {

        String result = resultFromSomeCondition(new Object());
        headers.getRequestHeaders().putSingle("X-HELLO", result);
        return "Hello World";
    }

    private String resultFromSomeCondition(Object condition) {
        return "World";
    }
}

Тогда ContainerResponseFilterможет выглядеть примерно так

@Provider
public class SingletonContainerResponseFilter 
                            implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext crc, 
            ContainerResponseContext crc1) throws IOException {
        String header = crc.getHeaderString("X-HELLO");
        crc1.getHeaders().putSingle("X-HELLO", "World");
    } 
}

И только так, что через этот фильтр проходят только одноэлементные классы, мы можем просто использовать @NameBindingаннотацию

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.ws.rs.NameBinding;

@NameBinding
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SingletonHeader {}

...

@SingletonHeader
public class SingletonResource {

...

@SingletonHeader
public class SingletonContainerResponseFilter 
                        implements ContainerResponseFilter {

Это единственный способ справиться с этой ситуацией.


Ресурсы:

Автор: Paul Samsotha Размещён: 25.12.2014 03:09

5 плюса

6833 Репутация автора

Решение

Правильный ответ, по-видимому, заключается в том, чтобы ввести HttpServletResponseпеременную-член реализации, как я уже отмечал, в другом сообщении .

@Context  //injected response proxy supporting multiple threads
private HttpServletResponse servletResponse;

Несмотря на то, что peeskillet указал, что полуофициальный список для Джерси не указан в HttpServletResponseкачестве одного из прокси-способных типов, когда я прослеживал код, по крайней мере RESTEasy, похоже, создает прокси ( org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy@xxxxxxxx). Так что, насколько я могу судить, кажется, что потокобезопасное внедрение единственной переменной-члена происходит.

См. Также https://stackoverflow.com/a/10076327/421049 .

Автор: Garret Wilson Размещён: 25.12.2014 04:05
Вопросы из категории :
32x32