Search code examples
jsonwildflyresteasybean-validationwildfly-8

Wildfly: ExceptionMapper not triggered with RestEasy JSR-303 Bean Validation


I'm using Bean Validation with RestEasy in Wildfly 8.2.0.Final:

@Path("/user")
@Produces(MediaType.APPLICATION_JSON)
public class UserEndpoint
{
    //more code

    @GET
    @Path("/encrypt/{email}")
    public Response fetchEncryptedId(@PathParam("email") @NotNull String email)
    {
        String encryptedUserId = userService.getEncryptedUserId(email);

        return Response.ok().entity(new UserBo(encryptedUserId)).build();
    }
}

This basically works. Now I'd like to get the response as JSON object but I can't get it working. All my "application" exceptions are handled by my Exception Mapper, this works:

@Provider
public class DefaultExceptionMapper implements ExceptionMapper<Exception>
{
    private static final String MEDIA_TYPE = "application/json";

    private LoggingService loggingService;

    @EJB
    public void setLoggingService(LoggingService loggingService)
    {
        this.loggingService = loggingService;
    }

    @Override
    public Response toResponse(Exception exception)
    {
        ResponseObject responseObject = new ResponseObject();
        responseObject.registerExceptionMessage(exception.getMessage());

        if (exception instanceof ForbiddenException)
        {
            loggingService.log(LogLevel.ERROR, ((ForbiddenException)exception).getUserId(), ExceptionToStringMapper.map(exception));
            return Response.status(Status.FORBIDDEN).type(MEDIA_TYPE).entity(responseObject).build();
        }

        //more handling

        loggingService.log(LogLevel.ERROR, "", ExceptionToStringMapper.map(exception));
        return Response.status(Status.INTERNAL_SERVER_ERROR).type(MEDIA_TYPE).entity(responseObject).build();
    }
}

But bean validation somehow bypasses it. Then I thought about using Throwable instead of Exception but it didn't help either. I guess the ExceptionMapper is not triggered because there is some life cycle problem with JAX-RS and JSR303. But how can I syncronize them to handle bean validation exceptions? Additional information: The exception passes the javax.ws.rs.container.ContainerResponseFilter so I could write some workaround by implementing the filter method in a subclass, but this is not clean solution. The target is to handle the exceptions in the Exception mapper.


Solution

  • It's not always the case that your ExceptionMapper<Exception> will catch all exception under the Exception hierarchy. If there is another more specific mapper, say one for RuntimeException, that mapper will be used for all exception of RuntimeException and its subtypes.

    That being said (assuming you're using resteasy-validation-provider-11), there is already a ResteasyViolationExceptionMapper that handles ValidationException.

    @Provider
    public class ResteasyViolationExceptionMapper 
            implements ExceptionMapper<ValidationException>
    

    This mapper is automatically registered. It returns results in the form of a ViolationReport. The client needs to set the Accept header to application/json in order to see a response similar to

    {
      "exception":null,
      "fieldViolations":[],
      "propertyViolations":[],
      "classViolations":[],
      "parameterViolations":[
        {
          "constraintType":"PARAMETER",
          "path":"get.arg0",
          "message":"size must be between 2 and 2147483647",
          "value":"1"}
      ],
      "returnValueViolations":[]
    }
    

    You can see more at Violation reporting.

    If you want to completely override this behavior, you can create a more specific mapper for ResteasyViolationException, which is the exception thrown by the RESTeasy validator

    @Provider
    public class MyValidationMapper
            implements ExceptionMapper<ResteasyViolationException> {
    
        @Override
        public Response toResponse(ResteasyViolationException e) {
    
        }
    }