【问题标题】:Why does JerseyTest throw ConstraintViolationException instead of returning 400 Bad Request?为什么 JerseyTest 会抛出 ConstraintViolationException 而不是返回 400 Bad Request?
【发布时间】:2016-10-17 19:39:40
【问题描述】:

我正在使用 Jersey 版本 2.23.2,但我无法弄清楚如何使用 JerseyTest 来测试验证失败时的响应。出于某种原因,下面的测试抛出了一个 ConstraintViolationException,它被包裹在一个 ProcessingException 中,而不是返回 400 错误请求。我可以修改测试以检查 ProcessingException 是否被抛出,但我真的想测试响应。当我在没有 JerseyTest 的情况下在 Grizzly 中运行 HelloResource 时,我得到了适当的 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

【问题讨论】:

  • 我无法重现该问题。是的,测试失败了,但这只是因为您忘记了方法参数上的@Valid 注释。因此,当您期望 400 时,您会得到 200。除此之外,测试正在运行。我正在使用泽西 2.23。你用的是什么版本?
  • 我更新了帖子以包含更多详细信息。我正在使用泽西岛 2.23.2。你是对的,我忘记了@Valid 注释(好抓!)。但是,我将它添加到我的测试中,仍然有同样的问题。我也在帖子中添加了异常的副本。
  • 看起来像客户端错误,而不是服务器。该请求甚至没有通过。如果您不使用 bean,它将起作用。只需使用字符串。 Entity.json(new ObjectMapper().writeValueAsString(new Hello(null)));
  • 也许 messagebodywriter 与验证有关。我无法重现该错误。也许我的项目配置有点不同
  • 看起来 MOXy 默认启用了验证。因此,由于我尝试创建的 JSON 未验证,因此它正确地向客户端抛出异常。通过将此方法添加到测试中,我可以关闭验证:@Override protected void configureClient(final ClientConfig config) { super.configureClient(config); config.register(new MoxyJsonConfig() .property(MarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE) .resolver()); }

标签: rest jersey jax-rs grizzly jersey-test-framework


【解决方案1】:

这是一个客户端问题。正如您所发现的那样,MOXy 默认启用了 bean 验证。因此,您正在客户端上进行 bean 验证。所以请求甚至没有通过,因为错误发生在客户端。你可以通过发送一个字符串来测试它

Entity.json("{}")

这应该可以消除错误。但是,如果您想使用 bean,正如您在评论中提到的,您应该使用 MOXy 禁用客户端上的 bean 验证

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

【讨论】:

    猜你喜欢
    • 2018-10-08
    • 1970-01-01
    • 1970-01-01
    • 2018-09-21
    • 2020-05-29
    • 2013-10-02
    • 2012-01-18
    • 2023-03-21
    • 2017-09-02
    相关资源
    最近更新 更多