Reputation: 4699
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.
Upvotes: 0
Views: 2170
Reputation: 1530
The accepted answer seems to be not correct (any more).
So we never have a chance to handle it.
Isn't true now. Check What seems to be the correct solution in https://stackoverflow.com/a/45478604/1271372.
I followed that, implemented ExceptionMapper<SomeParseException>
and registered it in the resource config register(ExceptionMapperImplementation.class, 1)
before JacksonFeature and it works like a charm. Note the priority 1.
Of course, this should return code 400. I had to customise the response body to be JSON, not plain text.
Upvotes: 0
Reputation: 208964
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.
Upvotes: 1