I'm using Jersey 2.22.1 to implement a REST API.
I have created a custom exception mapper to map a RuntimeException
to a particular HTTP response code of my choosing.
@Provider
public class MyExceptionMapper implements ExceptionMapper<RuntimeException>
{
@Override
public Response toResponse(RuntimeException exception)
{
....
}
}
My resource class is configured to take a number of parameters for a POST request.
@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)
{
...
}
}
When I send a POST request without any parameters:
curl -X POST http://192.168.0.2:9989/myApp/rest/api/1.0/test
I'm expecting my custom exception mapper to be invoked, but it isn't. Instead I get a bunch of 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$2.run(ServerRuntime.java:318)
at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
at org.glassfish.jersey.internal.Errors$1.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$1.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)
Can anyone explain why my custom exception mapper is not being invoked?
Note that if I throw a RuntimeException
in my resource method and then send a valid POST request, then the exception mapper is invoked. So I know if works to some extent.
UPDATE
I have discovered that if I add the following to the HTTP request:
-H "Content-Type: application/x-www-form-urlencoded"
then my ConstraintViolationExceptionMapper
is invoked.
@Provider
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException>
{
...
}
But if I remove the ConstraintViolationExceptionMapper
, my custom exception mapper is still not invoked.
Following the JAX-RS spec, for @FormParam
and other @XxxParam
s, when an exception occurs during the parsing of the param, Jersey throws an exception that is already mapped. This is to follow the spec in order to return the correct response status. So we never have a chance to handle it.
Jersey already has a mapper for ValidationException
which is a super class of ConstraintViolationException
. So when you provider the mapper for the ConstrainViolationException
, it is more specific than the one for ValidationException
, so it gets called.
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?
Because the param parsing occurs before the validation.