JAX-RS jersey ExceptionMappers User-Defined Except

2019-01-11 16:03发布

问题:

I am new to this, trying to achieve reading some docs but its not working, please bear with me.

I have created a UserNotFoundMapper using ExceptionMappers like this:

public class UserNotFoundMapper implements ExceptionMapper<UserNotFoundException> {

@Override
public Response toResponse(UserNotFoundException ex) {
    return Response.status(404).entity(ex.getMessage()).type("text/plain").build();
}

}

This in my service:

@GET
@Path("/user")
public Response getUser(@QueryParam("id") String id) throws UserNotFoundException{
    //Some user validation code with DB hit, if not found then
    throw new UserNotFoundException();
}

The UserNotFoundException is an User-Defined Exception.

I tried this:

public class UserNotFoundException extends Exception {
       //SOME block of code 
}

But when I invoke the service, the UserDefinedExceptionMapper is not getting invoked. It seems I might be missing something in the UserDefinedException. How to define this exception then?

Please let me know how to define the UserNotFoundException.

回答1:

You need to annotate your exception mapper with @Provider, otherwise it will never get registered with the JAX-RS runtime.

@Provider
public class UserNotFoundMapper implements
        ExceptionMapper<UserNotFoundException> {
    @Override
    public Response toResponse(UserNotFoundException ex) {
        return Response.status(404).entity(ex.getMessage()).type("text/plain")
                .build();
    }
}


回答2:

What I usually do when creating APIs is create my own exception that extends from RuntimeException so I don't necessarily have to catch my exception.

Here's an example:

NOTE: I'm using JAX-RS with Jersey

First: create my own Exception that extends from RuntimeException.

public class ExceptionName extends RuntimeException {

private int code;
private String message;

public int getCode(){
    return code;
}

public String getMessage(){
    return message;
}

public ExceptionName(int code, String message) {
    this.code = code;
    this.message = message;
}

}

Also implement a ExceptionMapper

@Provider
public class ExceptionName implements ExceptionMapper<ExceptionName>{

    @Override
    public Response toResponse(ExceptionName exception) {
        return Response.status(exception.getCode()).entity(exception.getMessage()).build();
    }

}

And every time that I want to throw an exception I just do it like this anywhere, the exception mapper will take care of returning a response to the client consuming the API

throw new ExceptionName(500,"there was an error with something here");


回答3:

One small remark , try to Use Response.Status.NOT_FOUND rather than using 404 etc. Code will be more readable and less prone to typos , the same goes for "text/plain". Below is the code that will handle exception as you mentioned. Oh and one more thing remember to annotate your method @Produces(MediaType.TEXT_PLAIN) in your interface


    public class UserNotFoundException extends Exception {
        //...
    }

    public class UserServiceImpl implements UserService {

        @Override 
        public Response getUser(@QueryParam("id") String id) {
            final Response response;
            try{
                // call user method
                //if everything is ok
                response = Response.status(Response.Status.OK).entity(whateverYouWant).type(MediaType.TEXT_PLAIN).build();
            } catch(UserNotFoundException ex) {         
                response = new UserNotFoundMapper().toResponse(ex);
            }

            return response;
        }
    }

    In client slide you can check 

    public static boolean isUserExists(final Response serverResp) {
        return serverResp != null && serverResp.getStatus() == Response.Status.NOT_FOUND.getStatusCode();
    }