RESTEasy Mock vs. Exception Mapper vs. Context

RESTEasy模拟框架在没有exception映射器的情况下工作正常 – 收到请求并返回具有预期内容的实体。

注册exception映射器并强制exception后,当RESTEasy的内部调用ResteasyProviderFactory.getContextData(type)时调用失败,返回null,导致意外的错误消息:“无法找到类型的上下文数据:javax.servlet.http.HttpServletRequest” 。

无法在网上任何地方找到RESTEasy模拟加上exception映射器的任何示例,也无法找到有关错误的任何有用信息。

客户类:

package com.foo; import javax.xml.bind.annotation.XmlAccessType; import javax.xml.bind.annotation.XmlAccessorType; import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlType; @XmlAccessorType(XmlAccessType.FIELD) @XmlType(name = "foo-type", propOrder = { "name" }) @XmlRootElement(name = "foo") public class Foo { protected String name; public String getName() { return name; } public void setName(String value) { this.name = value; } } 

对象工厂:

 package com.foo; import javax.xml.bind.annotation.XmlRegistry; @XmlRegistry public class ObjectFactory { public ObjectFactory() { } public Foo createFoo() { return new Foo(); } } 

validationexception:

 package com.foo; public class ValidationException extends RuntimeException { private static final long serialVersionUID = -8100360206713223313L; public ValidationException(String message) { super(message); } public ValidationException(Exception innerException) { super(innerException); } public ValidationException(String message, Exception innerException) { super(message, innerException); } } 

服务终点:

 package com.foo; import javax.ws.rs.GET; import javax.ws.rs.Path; import javax.ws.rs.Produces; @Path("/rest/v1") public class FooService { @GET @Path("/foo") @Produces("application/xml") public Foo alwaysBlowUp() throws ValidationException { if (System.currentTimeMillis() > 0) { throw new ValidationException("FOO"); } return null; } } 

exception映射器:

 package com.foo; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; import javax.ws.rs.core.Response.Status; import javax.ws.rs.ext.ExceptionMapper; import javax.ws.rs.ext.Provider; @Provider public class FooExceptionMapper implements ExceptionMapper { @Context private static HttpServletRequest request; @Context private static HttpHeaders headers; @Override public Response toResponse(ValidationException exception) { MediaType mediaType = null; /* Set breakpoint on line below. Step over line and you get the exception in the logs. Step into the line and the problem is in ResteasyProviderFactory: public static  T getContextData(Class type) { return (T) getContextDataMap().get(type); <<< type == javax.servlet.http.HttpServletRequest } The type is not in the map, so it returns null. The null results in this error in ContextParameterInjector: private class GenericDelegatingProxy implements InvocationHandler { public Object invoke(Object o, Method method, Object[] objects) throws Throwable { try { Object delegate = ResteasyProviderFactory.getContextData(type); if (delegate == null) throw new LoggableFailure("Unable to find contextual data of type: " + type.getName()); <<< ERROR IN LOGS */ String acceptHeader = request.getHeader("accept"); if (MediaType.APPLICATION_XML.equals(acceptHeader)) { mediaType = MediaType.APPLICATION_XML_TYPE; } else if (MediaType.APPLICATION_JSON.equals(acceptHeader)) { mediaType = MediaType.APPLICATION_JSON_TYPE; } else { mediaType = headers.getMediaType(); if (mediaType == null) { mediaType = MediaType.APPLICATION_XML_TYPE; } } ResponseBuilder builder = Response.status(Status.BAD_REQUEST); builder.type(mediaType); return builder.build(); } } 

测试:

 package com.foo; import java.net.URISyntaxException; import org.jboss.resteasy.core.Dispatcher; import org.jboss.resteasy.mock.MockDispatcherFactory; import org.jboss.resteasy.mock.MockHttpRequest; import org.jboss.resteasy.mock.MockHttpResponse; import org.jboss.resteasy.plugins.server.resourcefactory.POJOResourceFactory; public final class TestFooExceptionMapper { public static void main(String[] args) throws URISyntaxException { Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); dispatcher.getRegistry().addResourceFactory(new POJOResourceFactory(FooService.class)); dispatcher.getProviderFactory().addExceptionMapper(FooExceptionMapper.class); MockHttpRequest request = MockHttpRequest.get("/rest/v1/foo"); MockHttpResponse response = new MockHttpResponse(); dispatcher.invoke(request, response); } } 

错误:

 Aug 26, 2012 10:44:26 PM org.jboss.resteasy.core.SynchronousDispatcher SEVERE: Failed executing GET /rest/v1/foo org.jboss.resteasy.spi.LoggableFailure: Unable to find contextual data of type: javax.servlet.http.HttpServletRequest at org.jboss.resteasy.core.ContextParameterInjector$GenericDelegatingProxy.invoke(ContextParameterInjector.java:56) at $Proxy18.getHeader(Unknown Source) at com.foo.FooExceptionMapper.toResponse(FooExceptionMapper.java:51) at com.foo.FooExceptionMapper.toResponse(FooExceptionMapper.java:1) at org.jboss.resteasy.core.SynchronousDispatcher.executeExceptionMapper(SynchronousDispatcher.java:330) at org.jboss.resteasy.core.SynchronousDispatcher.unwrapException(SynchronousDispatcher.java:359) at org.jboss.resteasy.core.SynchronousDispatcher.handleApplicationException(SynchronousDispatcher.java:348) at org.jboss.resteasy.core.SynchronousDispatcher.handleException(SynchronousDispatcher.java:220) at org.jboss.resteasy.core.SynchronousDispatcher.handleInvokerException(SynchronousDispatcher.java:196) at org.jboss.resteasy.core.SynchronousDispatcher.getResponse(SynchronousDispatcher.java:551) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:513) at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:125) at com.foo.TestFooExceptionMapper.main(TestFooExceptionMapper.java:20) 

问题是“模拟框架”不向上下文提供HttpServletRequest 。 它与ExceptionMapper没有直接关系。

Resteasy根据ResteasyProviderFactory.getContextDataMap()存在的数据注入@Context字段。 当这个映射不包含HttpServletRequest的实例时,它会爆发错误“无法找到类型的上下文数据……”

我通过将一个模拟的HttpServletRequest放入resteasy上下文来解决这个问题:

 Dispatcher dispatcher = MockDispatcherFactory.createDispatcher(); dispatcher.getRegistry().addSingletonResource(new Service()); ResteasyProviderFactory .getContextDataMap() .put(HttpServletRequest.class, new MockHttpServletRequest()); MockHttpResponse response = new MockHttpResponse(); dispatcher.invoke( MockHttpRequest.get("/"), response );