Включение проверки ваших ресурсов в Dropwizard

Я использую Java 8 с Dropwizard 0.7.

Я не могу получить подтверждение для работы с моими ресурсами в Dropwizard

У меня есть следующий боб

@Data //lombok anntation
@JsonRootName(value="CreateNewGame")
@XmlRootElement
public class CreateNewGameDTO {
    @NotEmpty
    public String name;

    @NotNull
    private GameType type;
    @NotBlank
    private String username;

    @NotNull
    @Min(1)
    private Integer numOfPlayers;
}

И в моем классе ресурсов у меня есть следующий метод

@POST
@Timed
public Response createGame(@Valid CreateNewGameDTO dto) { ... }

My JUnit test

@ClassRule
public static final DropwizardAppRule<CivBoardGameRandomizerConfiguration> RULE =
        new DropwizardAppRule<CivBoardGameRandomizerConfiguration>(CivBoardgameRandomizerApplication.class, "src/main/resources/config.yml");

@Test
public void createGameShouldFailBecauseOfMissingUsername() throws Exception {
    List<NewCookie> cookies = performLogin();
    assertThat(cookies.size()).isEqualTo(2);

    Client client = Client.create();

    CreateNewGameDTO dto = new CreateNewGameDTO();
    dto.setNumOfPlayers(4);
    dto.setUsername(null); //Username NULL, so it should throw exception
    dto.setName("PBF WaW");
    dto.setType(GameType.WAW);

    ObjectMapper mapper = new ObjectMapper();
    String dtoAsJSon = mapper.writeValueAsString(dto);

    URI uri = UriBuilder.fromPath(String.format(BASE_URL + "/game", RULE.getLocalPort())).build();
    ClientResponse response = client.resource(uri)
            .type(MediaType.APPLICATION_JSON)
            .cookie(cookies.get(0))
            .cookie(cookies.get(1))
            .entity(dtoAsJSon)
            .post(ClientResponse.class);

    assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED_201);
    URI location = response.getLocation();
    assertTrue(location.getPath().matches(".*civilization/game/.*"));
}

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

ERROR [2014-10-01 11:03:39,515] com.sun.jersey.spi.container.ContainerResponse: A message body writer for Java class io.dropwizard.jersey.validation.ValidationErrorMessage, and Java type class io.dropwizard.jersey.validation.ValidationErrorMessage, and MIME media type application/xml was not found.
The registered message body writers compatible with the MIME media type are:
*/* ->
  com.sun.jersey.core.impl.provider.entity.FormProvider
  com.sun.jersey.core.impl.provider.entity.StringProvider
  com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
  com.sun.jersey.core.impl.provider.entity.FileProvider
  com.sun.jersey.core.impl.provider.entity.InputStreamProvider
  com.sun.jersey.core.impl.provider.entity.DataSourceProvider
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
  com.sun.jersey.core.impl.provider.entity.ReaderProvider
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.StreamingOutputProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
  com.sun.jersey.server.impl.template.ViewableMessageBodyWriter
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
  com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
application/xml ->
  com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$App
  com.sun.jersey.core.impl.provider.entity.DocumentProvider
  com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
  com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$App
  com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$App

Я ничего не делал, чтобы "зарегистрировать" валидацию. Я думал, что dropwizard делает это автоматически. В документации тоже ничего не нашел. Я использую версию Dropwizard 0.7.


person Shervin Asgari    schedule 01.10.2014    source источник
comment
Это работает, когда вы пропускаете часть ломбока?   -  person Jan Galinski    schedule 01.10.2014
comment
@JanGalinski Нет, конечно. Lombok генерирует шаблонный код. Это не должно было повлиять на это, да и не повлияло. Все еще получаю ту же ошибку   -  person Shervin Asgari    schedule 01.10.2014
comment
Я использую Java 8, возможно, поэтому. Хотя, я не могу представить, почему. Я также добавил свой тест JUnit   -  person Shervin Asgari    schedule 01.10.2014


Ответы (2)


Не могли бы вы попробовать добавить аннотацию @Produces к своему ресурсу, например. @Produces(MediaType.APPLICATION_JSON) либо над конкретным методом, либо над всем классом, и посмотрите, работает ли это? Кажется, я только что наткнулся на то же самое. Если это так, это означает, что Dropwizard не может определить MessageBodyWriter для использования для ответа на этот тупе (я думаю)

person Michael    schedule 01.10.2014
comment
У меня уже есть @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) @Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) на ресурсе моего класса - person Shervin Asgari; 01.10.2014
comment
Я удалил APPLICATION_XML в разделе «Производство и потребление», и это сработало. Я думаю, что это может быть ошибка Dropwizard, поскольку он должен был определить, какой тип я использую, поскольку я отправляю JSON как тип в клиенте. - person Shervin Asgari; 02.10.2014

Проблема в том, что возникает исключение, и никакой exceptionMapper не может сопоставить его с допустимым выходом. Вы можете попробовать это:

@Provider
@Produces(MediaType.TEXT_PLAIN)
public class ValidationErrorMessageBodyWriter implements MessageBodyWriter<ValidationErrorMessage> {

    private static final Logger LOG = LoggerFactory.getLogger(ValidationErrorMessageBodyWriter.class);

    @Override
    public boolean isWriteable(
        Class<?> type,
        Type genericType,
        Annotation[] annotations,
        MediaType mediaType)
    {
        return ValidationErrorMessage.class.isAssignableFrom(type);
    }

    @Override
    public long getSize(
        ValidationErrorMessage t,
        Class<?> type,
        Type genericType,
        Annotation[] annotations,
        MediaType mediaType)
    {
        return -1;
    }

    @Override
    public void writeTo(
        ValidationErrorMessage t,
        Class<?> type,
        Type genericType,
        Annotation[] annotations,
        MediaType mediaType,
        MultivaluedMap<String, Object> httpHeaders,
        OutputStream entityStream) throws IOException, WebApplicationException
    {
        Joiner joiner = Joiner.on("; ").skipNulls();
        String message = joiner.join(t.getErrors().iterator());
        entityStream.write(message.getBytes(Charsets.UTF_8));
        LOG.info(message);
    }

}

Теперь добавьте в свое приложение метод run(config,enviroment):

    // Serializer
    environment.jersey().register(new ValidationErrorMessageBodyWriter());      

Возможно, вы хотите изменить BodyWriter для получения другого вывода, такого как xml или sth. еще! Надеюсь, это решит вашу проблему :-)


EDIT: вопрос в комментарии

Добавьте это в один из ваших ресурсов:

@GET
@Path("getMBW")
public Response getInTestResource(@Context  MessageBodyWorkers workers)
{
    Map<MediaType, List<MessageBodyWriter>> writers = workers.getWriters(MediaType.WILDCARD_TYPE);

    for (Entry<MediaType, List<MessageBodyWriter>> entry : writers.entrySet()) {
        for (MessageBodyWriter  writer : entry.getValue()) {
            System.out.println(String.format("For mediaType '%s' there is a class called %s", entry.getKey(),writer.getClass().getName()));
        }
    }       
    return Response.status(Status.OK).type(MediaType.TEXT_PLAIN).entity("get is ok").build();
}

Тогда я получаю это:

For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.FormProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.MimeMultipartProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.StringProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.ByteArrayProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.FileProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.InputStreamProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.DataSourceProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.XMLJAXBElementProvider$General
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.ReaderProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.DocumentProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.StreamingOutputProvider
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.SourceProvider$SourceWriter
For mediaType '*/*' there is a class called com.sun.jersey.server.impl.template.ViewableMessageBodyWriter
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.XMLRootElementProvider$General
For mediaType '*/*' there is a class called com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$General
For mediaType '*/*' there is a class called com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider
person user3280180    schedule 16.10.2014
comment
Если кто-то спросит: -1 в getSize() -> Неотрицательное возвращаемое значение используется в заголовке HTTP Content-Length. Возвращает длину в байтах или -1, если длину нельзя определить заранее (источник: см. описание MessageBodyWriter) - person user3280180; 16.10.2014
comment
Но почему это работает, когда я удалил APPLICATION_XML в разделе «Производит и потребляет» без этого средства записи сообщений об ошибках проверки? - person Shervin Asgari; 17.10.2014
comment
Если вы удалите продукты, то Джерси может использовать другой преобразователь для запрошенных медиатипов. Каждый клиент отправляет заголовки Accepts, и я думаю, что для вывода использовался другой маппер. Возможно, вы можете проверить, принимает ли ваш клиент запрос заголовка для исследования. - person user3280180; 17.10.2014
comment
Я не удалял Produces, но у меня были JSON и XML как product/consume. Я удаляю XML и использую только JSON, и вдруг это сработало - person Shervin Asgari; 17.10.2014
comment
Да, у вас не было автора тела для application/xml, но было одно для json. Вот почему это сработало. - person user3280180; 17.10.2014
comment
Извините за все эти вопросы, но я хочу научиться :) Где JSOn body Writer? Он встроен в dropwizard? - person Shervin Asgari; 18.10.2014
comment
BodyWriter из Джерси, а Dropwizard использует Джерси. Но на данный момент я не знаю, как получить информацию о встроенных функциях майки в вашем приложении. - person user3280180; 20.10.2014
comment
Эта stackoverflow.com/a/26420051/3280180 - та же проблема, что и выше, описанная мной! - person user3280180; 20.10.2014