为什么 JerseyTest 抛出 ConstraintViolationException 而不是返回 400 Bad Request?

Why does JerseyTest throw ConstraintViolationException instead of returning 400 Bad Request?

我正在使用 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.call(JerseyInvocation.java:684)
    at org.glassfish.jersey.client.JerseyInvocation.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.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.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access[=11=]0(ParentRunner.java:58)
    at org.junit.runners.ParentRunner.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

这是客户端问题。正如您所发现的,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());
}