37

I have the following code:

@Path("/users/{id}")
public class UserResource {

    @Autowired
    private UserDao userDao;

    @GET
    @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
    public User getUser(@PathParam("id") int id) {
        User user = userDao.getUserById(id);
        if (user == null) {
            throw new NotFoundException();
        }
        return user;
    }

If I request for a user that doesn't exists, like /users/1234, with "Accept: application/json", this code returns an HTTP 404 response like one would expect, but returns Content-Type sets to text/html and a body message of html. Annotation @Produces is ignored.

Is it a problem of code or a problem of configuration?

nwinkler
  • 52,665
  • 21
  • 154
  • 168
IJR
  • 552
  • 1
  • 6
  • 10

3 Answers3

38

Your @Produces annotation is ignored because uncaught exceptions are processed by the jax-rs runtime using a predefined (default) ExceptionMapper If you want to customize the returned message in case of a specific exception you can create your own ExceptionMapper to handle it. In your case you need one to handle the NotFoundException exception and query the "accept" header for the requested type of the response:

@Provider
public class NotFoundExceptionHandler implements ExceptionMapper<NotFoundException>{

    @Context
    private HttpHeaders headers;

    public Response toResponse(NotFoundException ex){
        return Response.status(404).entity(yourMessage).type( getAcceptType()).build();
    }

    private String getAcceptType(){
         List<MediaType> accepts = headers.getAcceptableMediaTypes();
         if (accepts!=null && accepts.size() > 0) {
             //choose one
         }else {
             //return a default one like Application/json
         }
    }
}
Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
  • I tested it. I continue throwing NotFoundException, but now mapping that exception using ExceptionMapper to setting status code and media type. It works! Before this I tried to return an equivalent response instead throwing NotFoundException, but I have the same result, a text/html 404 response. Is this behaviour due to if my app throws a NotFoundException the default (implicit) ExceptionMapper comes in and override response that I returns to html response? Thanks! – IJR May 26 '14 at 19:10
  • 2
    After trying different solution, this is the best and correct solution. It is not obligatory to map *NotFoundException*. Just use Exception and do what you need – Valijon Jul 28 '15 at 13:16
  • @Provider is very important – Mohamd Ali Feb 04 '18 at 20:43
18

You can use the Response return. Example below:

@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Response get(@PathParam("id") Long id) {
    ExampleEntity exampleEntity = getExampleEntityById(id);

    if (exampleEntity != null) {
        return Response.ok(exampleEntity).build();
    }

    return Response.status(Status.NOT_FOUND).build();
}
Tiago
  • 691
  • 7
  • 12
0

that 404 is returned by your server as it is expected that you will pass things in following form

/users/{id}

but you are passing it as

/users/user/{id}

which resource is not existing at all

try accessing resource as /users/1234

EDIT:

create a class like

class RestResponse<T>{
private String status;
private String message;
private List<T> objectList;
//gettrs and setters
}

now in case you want response for User you can create it as following

RestResponse<User> resp = new RestResponse<User>();
resp.setStatus("400");
resp.setMessage("User does not exist");

and signature of your rest method would be like following

public RestResponse<User> getUser(@PathParam("id") int id)

while in case successful response you can set things like

RestResponse<User> resp = new RestResponse<User>();
List<User> userList = new ArrayList<User>();
userList.add(user);//the user object you want to return
resp.setStatus("200");
resp.setMessage("User exist");
resp.setObjectList(userList);
dev2d
  • 4,245
  • 3
  • 31
  • 54
  • URI requested updated, I made a mistake typing URI. The correct URI is users/1234 to get 404 response in html but not in JSON. My problem is not response code but format of response (content-type). – IJR May 25 '14 at 18:33
  • remove MediaType.APPLICATION_XML from produces and try, also make sure you are giving proper message in case user is not existing. usually we create a rest response which will contain status and message for user's request and we return the same as a response when someone tries accessing resource – dev2d May 25 '14 at 18:35
  • I tried to remove MediaType.APPLICATION_XML from annotation @Produces and I get the same result, a 404 Response with text/html Content-Type. It's like Tomcat is overriden content-type and body message... – IJR May 25 '14 at 18:41
  • as i mentioned in above comment in case user is not existing you need to pass an object of a class which contains status and message there you need to set status explicitly will edit ans – dev2d May 25 '14 at 18:42
  • Maybe I will mix two approaches (VD' & Svetlin Zarev), using an ExceptionMapper with an entity that is not only a string message but an object with some properties like status code, message, etc. Thanks! – IJR May 26 '14 at 19:15