Использование провайдеров по умолчанию /MessageBodyWriters в Джерси 2

Просто начиная с Джерси, я пытался воспроизвести простой пример из последней документации Джерси " Ответы на строительство". Эта часть, насколько я понимаю, должна показать, как Response а также ResponseBuilder может быть использован для простого возврата ответа в сочетании с Entity<T> для содержания ответа.

Теперь в документации говорится, что по умолчанию поддерживаются несколько типов данных (здесь: " Представления и типы Java"). String премьер среди них, соответствующий любому типу СМИ.

Из всех вариантов, которые я пробовал, следующее является самым простым:

@POST
public Response post() {
    URI createdUri;
    try {
        createdUri = new URI("http://test.lan");
    } catch (final URISyntaxException e) {
        throw new WebApplicationException(e);
    }

    return Response.created(createdUri).entity(Entity.text("someContent")).build();
}

Я всегда получал одну и ту же ошибку (полная трассировка стека ниже) при вызове запроса: org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=text/plain, type=class javax.ws.rs.client.Entity, genericType=class javax.ws.rs.client.Entity.

Я полагаю, это говорит о том, что подходящего поставщика не найдено для этого универсального типа сущности. Тем не менее, строка должна поддерживаться OOTB?

я нашел это StringMessageProvider это, вероятно, реализация этого провайдера на Джерси 1, и наиболее близкие родственные классы, которые я нашел в моих библиотеках на Джерси 2, являются классами в org.glassfish.jersey.message.internal в майке Среди многих провайдеров есть StringMessageProvider, который представляется мне как потенциальный предполагаемый поставщик для этого.

Я посмотрел на проблему, и хотя есть много людей, которые получают это, когда неправильно пытаются использовать пользовательский провайдер, я не нашел ничего о неработающих OOTB-провайдерах по умолчанию.

Я проверил свои библиотеки и сейчас у меня есть следующие зависимости в моем pom (среди прочих):

  • Джерси-контейнер-сервлет-жильный
  • Джерси-клиент
  • Джерси-общий
  • Джерси-сервер

Я посмотрел онлайн, но, похоже, это все, что мне нужно, хотя я с уверенностью не нашел подходящие классы провайдеров для String и JAXB/JSON в банках.

контекст

  • Maven проект
  • с сервлетом api tomcat 6.0.29
  • Версия 2.6 всех упомянутых библиотек Джерси
  • Затмение Кеплер
  • Использование плагина tomcat6 maven для запуска встроенного tomcat (пока работает нормально)

Запрос Fiddler, используемый для тестирования

POST HTTP/1.1
User-Agent: Fiddler
Host: 127.0.0.1
Content-Length: 0

И снова попробовал несколько вариантов.

Полная трассировка стека

06-Jan-2015 21:13:54 org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor aroundWriteTo
SEVERE: MessageBodyWriter not found for media type=text/plain, type=class javax.ws.rs.client.Entity, genericType=class javax.ws.rs.client.Entity.
06-Jan-2015 21:13:54 org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet TestService threw exception
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=text/plain, type=class javax.ws.rs.client.Entity, genericType=class javax.ws.rs.client.Entity.
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:247)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
    at org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo(JsonWithPaddingInterceptor.java:103)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
    at org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo(MappableExceptionWrapperInterceptor.java:88)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1154)
    at org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse(ServerRuntime.java:571)
    at org.glassfish.jersey.server.ServerRuntime$Responder.processResponse(ServerRuntime.java:378)
    at org.glassfish.jersey.server.ServerRuntime$Responder.process(ServerRuntime.java:368)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:262)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:319)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:236)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1028)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:373)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:381)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:344)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:219)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:859)
    at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:602)
    at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:489)
    at java.lang.Thread.run(Thread.java:662)

РЕДАКТИРОВАТЬ

Та же самая ошибка (для application/json) происходит теперь, когда я аннотировал класс @XmlRootElement и попробуйте вернуть его в соответствии с документами Джерси:

@GET
@Produces(MediaType.APPLICATION_JSON) 
public Foo sampleFoo() {
    Foo foo = new Foo();

    return foo;
}

куда Foo помечен @XmlRootElement,

Я также добавил в качестве зависимости jersey-media-json-jackson, который, как я вижу, содержит явного поставщика JSONJaxb. Тем не менее, кажется, что это не так.

3 ответа

Решение

Первая проблема:

javax.ws.rs.client.Entityэто класс на стороне клиента. Спецификация JAX-RS ничего не говорит о его использовании на стороне сервера. Но я могу подтвердить многими различными тестами, что результат будет похож на то, что вы видите (по крайней мере, с Джерси). С Resteasy, он просто отправит Entity.toString()

Так как это не работает ни для Resteasy, ни для Jersey, я не буду говорить, что это ошибка, но возможна ошибка в документации Jersey, в которой приведен пример ее использования следующим образом:

@POST
@Consumes("application/xml")
public Response post(String content) {
  URI createdUri = ...
  String createdContent = create(content);
  return Response.created(createdUri)
                         .entity(Entity.text(createdContent)).build();
}

Вышесказанное не удалось и для меня. Но вы не ошиблись, сказав

... по умолчанию поддерживается несколько типов данных

как они есть. Чтобы ваш пример работал, просто измените Entity.text("someContent") просто "someContent"

return Response.created(createdUri).entity("someContent").build();

И просто для полноты, использование на стороне клиента может выглядеть примерно так

Response response = webTarget.request().post(Entity.text("Hello World"));

который работает просто отлично.

Второй выпуск:

До (я считаю) Джерси 2.9, jersey-media-json-jackson модуль не настроен автоматически. Таким образом, с 2.6 нам нужно настроить конфигурацию либо путем сканирования пакетов в web.xml или в Application подкласс. В любом случае, web.xml требуется. Как указано здесь в отношении среды сервлетов 2.x, которой является Tomcat 6.

В среде Servlet 2.5 вы должны явно объявить сервлет Jersey-контейнера в файле дескриптора развертывания web.xml вашего веб-приложения.

Поэтому для поиска классов провайдера JSON необходимо указать пакет в jersey.config.server.provider.packages INIT-пары. Пример web.xml будет примерно таким

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
                       http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>Jersey Web Application</servlet-name>
        <servlet-class>
            org.glassfish.jersey.servlet.ServletContainer
        </servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>
                thepackage.of.your.resources,
                org.codehaus.jackson.jaxrs      <!-- Jackson providers -->
            </param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Jersey Web Application</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
</web-app>

Вам также разрешено использовать Application подкласс (который ResourceConfig продолжается от). Нам просто нужно указать это в web.xml, Пример конфигурации может быть что-то вроде

public class MyApplication extends ResourceConfig {  
    public MyApplication() {
        register(JacksonFeature.class);
        packages("thepackage.of.your.resources");
    }
}

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
                       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                       http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
    <servlet>
        <servlet-name>MyApplication</servlet-name>
        <servlet-class>
            org.glassfish.jersey.servlet.ServletContainer
        </servlet-class>
        <init-param>
            <param-name>javax.ws.rs.Application</param-name>
            <param-value>jersey2.tomcat6.MyApplication</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>MyApplication</servlet-name>
        <url-pattern>/api/*</url-pattern>
    </servlet-mapping>
</web-app>

Примечание. Все это было протестировано в той же среде, кроме Eclipse. Я использую Netbeans, хотя это не должно иметь никакого значения. Кроме того, единственные зависимости Maven, в которых я нуждался, были

<dependencies>
    <dependency>
        <groupId>org.glassfish.jersey.containers</groupId>
        <artifactId>jersey-container-servlet-core</artifactId>
        <version>${jersey.version}</version>
    </dependency>
    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
        <version>${jersey.version}</version>
    </dependency>
</dependencies>

<jersey.version>2.6</jersey.version>

С другой стороны, чтобы упростить разработку, я просто создал простой архетип Maven со следующими координатами

GroupId:     org.glassfish.jersey.archetypes
ArtifactId:  jersey-quickstart-webapp
Version:     2.6

Вы также можете увидеть Создание нового проекта из Maven Archetype

Что касается формата text/plain, это работает?

return Response.created(createdUri).type(MediaType.TEXT_PLAIN).entity("someContent").build();

Для вывода JSON у меня есть эти зависимости

    <dependency>
        <groupId>org.glassfish.jersey.media</groupId>
        <artifactId>jersey-media-json-jackson</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
    </dependency>

а также реализация Jaxb. Любой подойдет, пользуюсь

    <dependency>
        <groupId>com.sun.xml.bind</groupId>
        <artifactId>jaxb-impl</artifactId>
    </dependency>

Я также определяю провайдера сопоставления объектов, хотя я не уверен на 100%, что это требуется (если вы не хотите настраивать):

import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.type.TypeFactory;
import com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector;

@Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
    ObjectMapper mapper;

    public ObjectMapperProvider() {
        mapper = new ObjectMapper();
        mapper.configure( SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false );
        mapper.configure( SerializationFeature.INDENT_OUTPUT, true );
        mapper.configure( SerializationFeature.WRITE_NULL_MAP_VALUES, true );
        mapper.configure( SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, true );
        mapper.setAnnotationIntrospector( new JaxbAnnotationIntrospector(TypeFactory.defaultInstance()) );
    }

    @Override
    public ObjectMapper getContext(Class<?> type) {
        return mapper;
    }
}

Кроме того, я думаю, что вам нужно зарегистрировать функцию Джексона:

@ApplicationPath("")
public class Application extends ResourceConfig {
    public Application() {
        register( JacksonFeature.class );
    }
}

Должен отметить, что все это настроено с использованием Jersey 2.11.

В моем случае я добавил зависимость jersey-media-json-jackson. Это сработало для меня.

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>2.20</version>
</dependency>
Другие вопросы по тегам