Jersey - 自定义异常映射器未在无效 POST 请求上调用
Jersey - Custom exception mapper not invoked on invalid POST request
我正在使用 Jersey 2.22.1 来实现 REST API。
我创建了一个自定义异常映射器来将 RuntimeException
映射到我选择的特定 HTTP 响应代码。
@Provider
public class MyExceptionMapper implements ExceptionMapper<RuntimeException>
{
@Override
public Response toResponse(RuntimeException exception)
{
....
}
}
我的资源 class 配置为采用 POST 请求的多个参数。
@Path("rest/api/1.0/test")
public class MyResource
{
@NotNull(message = "Missing parameter 'param1'")
@FormParam("param1")
private String m_param1;
@NotNull(message = "Missing parameter 'param2'")
@FormParam("param2")
private String m_param2;
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes("application/x-www-form-urlencoded")
public Response test(String body)
{
...
}
}
当我发送不带任何参数的 POST 请求时:
curl -X POST http://192.168.0.2:9989/myApp/rest/api/1.0/test
我期待我的自定义异常映射器被调用,但事实并非如此。相反,我得到了一堆 IllegalStateException
.
WARNING: The following warnings have been detected: WARNING: Unknown HK2 failure detected:
MultiException stack 1 of 6
java.lang.IllegalStateException: The @FormParam is utilized when the content type of the request entity is not application/x-www-form-urlencoded
at org.glassfish.jersey.server.internal.inject.FormParamValueFactoryProvider$FormParamValueFactory.ensureValidRequest(FormParamValueFactoryProvider.java:183)
at org.glassfish.jersey.server.internal.inject.FormParamValueFactoryProvider$FormParamValueFactory.getForm(FormParamValueFactoryProvider.java:167)
at org.glassfish.jersey.server.internal.inject.FormParamValueFactoryProvider$FormParamValueFactory.provide(FormParamValueFactoryProvider.java:118)
at org.glassfish.jersey.server.internal.inject.ParamInjectionResolver.resolve(ParamInjectionResolver.java:134)
at org.jvnet.hk2.internal.ClazzCreator.resolve(ClazzCreator.java:211)
at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:234)
at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:357)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:162)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2072)
at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:767)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:706)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:172)
at org.glassfish.jersey.server.model.MethodHandler$ClassBasedMethodHandler.getInstance(MethodHandler.java:284)
at org.glassfish.jersey.server.internal.routing.PushMethodHandlerRouter.apply(PushMethodHandlerRouter.java:74)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:109)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:92)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:61)
at org.glassfish.jersey.process.internal.Stages.process(Stages.java:197)
at org.glassfish.jersey.server.ServerRuntime.run(ServerRuntime.java:318)
at org.glassfish.jersey.internal.Errors.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors.call(Errors.java:267)
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:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:384)
at org.glassfish.grizzly.http.server.HttpHandler.run(HttpHandler.java:224)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
at java.lang.Thread.run(Thread.java:745)
谁能解释为什么我的自定义异常映射器没有被调用?
请注意,如果我在我的资源方法中抛出一个 RuntimeException
,然后发送一个有效的 POST 请求,则会调用异常映射器。所以我知道是否在某种程度上有效。
更新
我发现如果我在 HTTP 请求中添加以下内容:
-H "Content-Type: application/x-www-form-urlencoded"
然后调用我的 ConstraintViolationExceptionMapper
。
@Provider
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException>
{
...
}
但是如果我删除 ConstraintViolationExceptionMapper
,我的自定义异常映射器仍然不会被调用。
更新错误前
遵循JAX-RS规范,对于@FormParam
和其他@XxxParam
s,当param解析过程中发生异常时,Jersey会抛出一个已经映射的异常。这是为了遵循规范,以便 return 正确的响应状态。所以我们永远没有机会处理它。
更新有问题
泽西岛已经有一个 mapper for ValidationException
,它是 ConstraintViolationException
的超级 class。因此,当您为 ConstrainViolationException
提供映射器时,它比 ValidationException
的映射器更具体,因此它会被调用。
Even if I provide a mapper for ValidationException
, it is not invoked when I send a request without a header. Do you know why that is?
因为参数解析发生在验证之前。
接受的答案似乎不正确(不再)。
So we never have a chance to handle it.
现在不是这样。检查在.
中似乎是正确的解决方案
我遵循了这一点,实现了 ExceptionMapper<SomeParseException>
并在 JacksonFeature 之前在资源配置 register(ExceptionMapperImplementation.class, 1)
中注册了它,它就像一个魅力。注意优先级1.
当然,这应该 return 代码 400。我必须将响应正文自定义为 JSON,而不是纯文本。
我正在使用 Jersey 2.22.1 来实现 REST API。
我创建了一个自定义异常映射器来将 RuntimeException
映射到我选择的特定 HTTP 响应代码。
@Provider
public class MyExceptionMapper implements ExceptionMapper<RuntimeException>
{
@Override
public Response toResponse(RuntimeException exception)
{
....
}
}
我的资源 class 配置为采用 POST 请求的多个参数。
@Path("rest/api/1.0/test")
public class MyResource
{
@NotNull(message = "Missing parameter 'param1'")
@FormParam("param1")
private String m_param1;
@NotNull(message = "Missing parameter 'param2'")
@FormParam("param2")
private String m_param2;
@POST
@Produces(MediaType.TEXT_PLAIN)
@Consumes("application/x-www-form-urlencoded")
public Response test(String body)
{
...
}
}
当我发送不带任何参数的 POST 请求时:
curl -X POST http://192.168.0.2:9989/myApp/rest/api/1.0/test
我期待我的自定义异常映射器被调用,但事实并非如此。相反,我得到了一堆 IllegalStateException
.
WARNING: The following warnings have been detected: WARNING: Unknown HK2 failure detected:
MultiException stack 1 of 6
java.lang.IllegalStateException: The @FormParam is utilized when the content type of the request entity is not application/x-www-form-urlencoded
at org.glassfish.jersey.server.internal.inject.FormParamValueFactoryProvider$FormParamValueFactory.ensureValidRequest(FormParamValueFactoryProvider.java:183)
at org.glassfish.jersey.server.internal.inject.FormParamValueFactoryProvider$FormParamValueFactory.getForm(FormParamValueFactoryProvider.java:167)
at org.glassfish.jersey.server.internal.inject.FormParamValueFactoryProvider$FormParamValueFactory.provide(FormParamValueFactoryProvider.java:118)
at org.glassfish.jersey.server.internal.inject.ParamInjectionResolver.resolve(ParamInjectionResolver.java:134)
at org.jvnet.hk2.internal.ClazzCreator.resolve(ClazzCreator.java:211)
at org.jvnet.hk2.internal.ClazzCreator.resolveAllDependencies(ClazzCreator.java:234)
at org.jvnet.hk2.internal.ClazzCreator.create(ClazzCreator.java:357)
at org.jvnet.hk2.internal.SystemDescriptor.create(SystemDescriptor.java:471)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:162)
at org.jvnet.hk2.internal.Utilities.createService(Utilities.java:2072)
at org.jvnet.hk2.internal.ServiceLocatorImpl.internalGetService(ServiceLocatorImpl.java:767)
at org.jvnet.hk2.internal.ServiceLocatorImpl.getService(ServiceLocatorImpl.java:706)
at org.glassfish.jersey.internal.inject.Injections.getOrCreate(Injections.java:172)
at org.glassfish.jersey.server.model.MethodHandler$ClassBasedMethodHandler.getInstance(MethodHandler.java:284)
at org.glassfish.jersey.server.internal.routing.PushMethodHandlerRouter.apply(PushMethodHandlerRouter.java:74)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:109)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage._apply(RoutingStage.java:112)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:92)
at org.glassfish.jersey.server.internal.routing.RoutingStage.apply(RoutingStage.java:61)
at org.glassfish.jersey.process.internal.Stages.process(Stages.java:197)
at org.glassfish.jersey.server.ServerRuntime.run(ServerRuntime.java:318)
at org.glassfish.jersey.internal.Errors.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors.call(Errors.java:267)
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:267)
at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
at org.glassfish.jersey.grizzly2.httpserver.GrizzlyHttpContainer.service(GrizzlyHttpContainer.java:384)
at org.glassfish.grizzly.http.server.HttpHandler.run(HttpHandler.java:224)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:591)
at org.glassfish.grizzly.threadpool.AbstractThreadPool$Worker.run(AbstractThreadPool.java:571)
at java.lang.Thread.run(Thread.java:745)
谁能解释为什么我的自定义异常映射器没有被调用?
请注意,如果我在我的资源方法中抛出一个 RuntimeException
,然后发送一个有效的 POST 请求,则会调用异常映射器。所以我知道是否在某种程度上有效。
更新
我发现如果我在 HTTP 请求中添加以下内容:
-H "Content-Type: application/x-www-form-urlencoded"
然后调用我的 ConstraintViolationExceptionMapper
。
@Provider
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException>
{
...
}
但是如果我删除 ConstraintViolationExceptionMapper
,我的自定义异常映射器仍然不会被调用。
更新错误前
遵循JAX-RS规范,对于@FormParam
和其他@XxxParam
s,当param解析过程中发生异常时,Jersey会抛出一个已经映射的异常。这是为了遵循规范,以便 return 正确的响应状态。所以我们永远没有机会处理它。
更新有问题
泽西岛已经有一个 mapper for ValidationException
,它是 ConstraintViolationException
的超级 class。因此,当您为 ConstrainViolationException
提供映射器时,它比 ValidationException
的映射器更具体,因此它会被调用。
Even if I provide a mapper for
ValidationException
, it is not invoked when I send a request without a header. Do you know why that is?
因为参数解析发生在验证之前。
接受的答案似乎不正确(不再)。
So we never have a chance to handle it.
现在不是这样。检查在
我遵循了这一点,实现了 ExceptionMapper<SomeParseException>
并在 JacksonFeature 之前在资源配置 register(ExceptionMapperImplementation.class, 1)
中注册了它,它就像一个魅力。注意优先级1.
当然,这应该 return 代码 400。我必须将响应正文自定义为 JSON,而不是纯文本。