Почему JerseyTest выдает исключение ConstraintViolationException вместо возврата 400 неверных запросов?

#rest #jersey #jax-rs #grizzly #jersey-test-framework

#rest #джерси #jax-rs #grizzly #jersey-test-framework

Вопрос:

Я использую версию Jersey 2.23.2 и не могу понять, как использовать JerseyTest для проверки ответов, когда проверка завершается неудачно. По какой-то причине приведенный ниже тест выдает a ConstraintViolationException , который завернут в a ProcessingException вместо возврата 400 неверного запроса. Я мог бы изменить тест, чтобы проверить, что ProcessingException выбрасывается, но я действительно хочу проверить ответ. Когда я запускаю HelloResource Grizzly без JerseyTest , я получаю соответствующий 400 ответ на неверный запрос. Есть идеи о том, как исправить badRequestResponse() приведенный ниже тест?

 package example;

import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.server.ServerProperties;
import org.glassfish.jersey.test.JerseyTest;
import org.junit.Test;

import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

import static org.junit.Assert.assertEquals;

public class BadRequestTest extends JerseyTest {
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Hello {
        @NotNull(message = "Name is a required field.")
        private final String name;

        private Hello() {
            this(null);
        }

        public Hello(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }
    }

    @Path("hello")
    public static class HelloResource {
        @POST
        public String sayHelloToMe(@Valid Hello hello) {
            return "Hello "   hello.getName()   "!";
        }
    }

    @Override
    protected Application configure() {
        return new ResourceConfig(HelloResource.class).property(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
    }

    /** Test OK Response. This Works!*/
    @Test
    public void okResponse() {
        Response response = target("hello")
                .request(MediaType.TEXT_PLAIN)
                .post(Entity.json(new Hello("Tiny Tim")));

        assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
        assertEquals("Hello Tiny Tim!", response.readEntity(String.class));
    }

    /** Test Bad Request Response.  This Fails! */
    @Test
    public void badRequestResponse() {
        Response response = target("hello")
                .request(MediaType.TEXT_PLAIN)
                .post(Entity.json(new Hello(null)));

        assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
    }
}
  

Вот исключение, которое я получаю:

 javax.ws.rs.ProcessingException: 
Exception Description: Constraints violated on marshalled bean:
example.BadRequestTest$Hello@456abb66
-->Violated constraint on property name: "Name is a required field.".
Internal Exception: javax.validation.ConstraintViolationException

    at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:261)
    at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:684)
    at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:681)
    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:228)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444)
    at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:681)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:437)
    at org.glassfish.jersey.client.JerseyInvocation$Builder.post(JerseyInvocation.java:343)
    at example.BadRequestTest.badRequestResponse(BadRequestTest.java:70)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: Exception [EclipseLink-7510] (Eclipse Persistence Services - 2.6.0.v20150309-bf26070): org.eclipse.persistence.exceptions.BeanValidationException
Exception Description: Constraints violated on marshalled bean:
example.BadRequestTest$Hello@456abb66
-->Violated constraint on property name: "Name is a required field.".
Internal Exception: javax.validation.ConstraintViolationException
    at org.eclipse.persistence.exceptions.BeanValidationException.constraintViolation(BeanValidationException.java:53)
    at org.eclipse.persistence.jaxb.JAXBBeanValidator.buildConstraintViolationException(JAXBBeanValidator.java:385)
    at org.eclipse.persistence.jaxb.JAXBBeanValidator.validate(JAXBBeanValidator.java:273)
    at org.eclipse.persistence.jaxb.JAXBMarshaller.validateAndTransformIfNeeded(JAXBMarshaller.java:588)
    at org.eclipse.persistence.jaxb.JAXBMarshaller.marshal(JAXBMarshaller.java:481)
    at org.eclipse.persistence.jaxb.rs.MOXyJsonProvider.writeTo(MOXyJsonProvider.java:949)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo(WriterInterceptorExecutor.java:265)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo(WriterInterceptorExecutor.java:250)
    at org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed(WriterInterceptorExecutor.java:162)
    at org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo(MessageBodyFactory.java:1130)
    at org.glassfish.jersey.client.ClientRequest.doWriteEntity(ClientRequest.java:517)
    at org.glassfish.jersey.client.ClientRequest.writeEntity(ClientRequest.java:499)
    at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:388)
    at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:285)
    at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:252)
    ... 39 more
Caused by: javax.validation.ConstraintViolationException
    at org.eclipse.persistence.jaxb.JAXBBeanValidator.buildConstraintViolationException(JAXBBeanValidator.java:383)
    ... 52 more
  

Комментарии:

1. Я не могу воспроизвести проблему. Тест завершается неудачей, да, но только потому, что вы забыли @Valid аннотацию к параметру метода. Таким образом, вы получаете 200, когда ожидаете 400. Кроме этого, тест работает. Я использую Jersey 2.23. Какую версию вы используете?

2. Я обновил сообщение, включив в него более подробную информацию. Я использую Jersey 2.23.2. Вы правы, что я забыл @Valid аннотацию (хороший улов!). Тем не менее, я добавил его в свой тест и все еще сталкиваюсь с той же проблемой. Я также добавил копию исключения в сообщение.

3. Похоже на ошибку на стороне клиента, а не сервера. Запрос даже не выполняется. Это будет работать, если вы не используете компонент. Просто используйте строку. Entity.json(new ObjectMapper().writeValueAsString(new Hello(null)));

4. Возможно, messagebodywriter имеет какое-то отношение к проверке. Однако я не смог воспроизвести эту ошибку. Возможно, конфигурация моего проекта была немного другой

5. Похоже, у MOXy по умолчанию включена проверка. Итак, он правильно генерирует исключение для клиента, поскольку JSON, который я пытаюсь создать, не проверяется. Добавив этот метод в тест, я могу отключить проверку: @Override protected void configureClient(final ClientConfig config) { super.configureClient(config); config.register(new MoxyJsonConfig() .property(MarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE) .resolver()); }

Ответ №1:

Это проблема на стороне клиента. Как вы обнаружили, у MOXy по умолчанию включена проверка компонента. Итак, вы получаете проверку компонента на клиенте. Таким образом, запрос даже не выполняется, поскольку ошибка возникает на клиенте. Вы можете проверить это, просто отправив строку

 Entity.json("{}")
  

Это должно устранить ошибку. Но если вы хотите использовать компонент, как вы упомянули в комментарии, вам следует отключить проверку компонента на клиенте с помощью MOXy

 @Override 
protected void configureClient(final ClientConfig config) {  
    super.configureClient(config);
    config.register(new MoxyJsonConfig() 
            .property(MarshallerProperties.BEAN_VALIDATION_MODE,
                      BeanValidationMode.NONE).resolver());
}