97

I'm using Spring Boot with @ResponseBody based approach like the following:

@RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public @ResponseBody Response getData(@PathVariable(ID_PARAMETER) long id, HttpServletResponse res) {
    Video video = null;
    Response response = null;
    video = videos.get(id - 1);
    if (video == null) {
      // TODO how to return 404 status
    }
    serveSomeVideo(video, res);
    VideoSvcApi client =  new RestAdapter.Builder()
            .setEndpoint("http://localhost:8080").build().create(VideoSvcApi.class);
    response = client.getData(video.getId());
    return response;
}

public void serveSomeVideo(Video v, HttpServletResponse response) throws IOException  {
    if (videoDataMgr == null) {
        videoDataMgr = VideoFileManager.get();
    }
    response.addHeader("Content-Type", v.getContentType());
    videoDataMgr.copyVideoData(v, response.getOutputStream());
    response.setStatus(200);
    response.addHeader("Content-Type", v.getContentType());
}

I tried some typical approaches as:

res.setStatus(HttpStatus.NOT_FOUND.value());
new ResponseEntity(HttpStatus.BAD_REQUEST);

but I need to return Response.

How to return here 404 status code if video is null?

catch23
  • 17,519
  • 42
  • 144
  • 217

4 Answers4

164

This is very simply done by throwing org.springframework.web.server.ResponseStatusException:

throw new ResponseStatusException(
  HttpStatus.NOT_FOUND, "entity not found"
);

It's compatible with @ResponseBody and with any return value. Requires Spring 5+

dur
  • 15,689
  • 25
  • 79
  • 125
andrej
  • 4,518
  • 2
  • 39
  • 39
  • 1
    Is there some way of doing this without causing Springboot to issue a warning? I get ` WARN 27604 --- [nio-8081-exec-9] .w.s.m.a.ResponseStatusExceptionResolver : Resolved [org.springframework.web.server.ResponseStatusException: 404 NOT_FOUND ` which finds its way into alerts etc unreasonably: this is not a problem. – GreenAsJade Sep 17 '19 at 02:46
  • @GreenAsJade I am not sure where is this warning logged (I have not seen this warning in my logs) but maybe you can play with log4j settings or talk to your alert system to avoid this. – andrej Sep 18 '19 at 08:22
  • 6
    This is not the best, your visitors will see the error stack information. – BuffK Nov 20 '19 at 08:13
  • 1
    @BuffK this is for web services (talking to machines) – andrej Mar 07 '20 at 22:56
  • This requires overring the /error endpoint. – Mindaugas Bernatavičius Feb 13 '22 at 08:03
109

Create a NotFoundException class with an @ResponseStatus(HttpStatus.NOT_FOUND) annotation and throw it from your controller.

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "video not found")
public class VideoNotFoundException extends RuntimeException {
}
chrylis -cautiouslyoptimistic-
  • 75,269
  • 21
  • 115
  • 152
  • 7
    Need the standard imports means:import org.springframework.http.HttpStatus; – Jean-Marc Astesana Feb 04 '18 at 15:02
  • 2
    @andrej That's a good solution some of the time, but (1) `ResponseStatusException` was not added until Spring 5; (2) A `NotFoundException` can be useful for either being caught or for a dual-stack HTML UI with an `@ExceptionHandler`. – chrylis -cautiouslyoptimistic- Apr 10 '19 at 15:26
  • 2
    This solution tightly couples the Exception to your REST-Controller layer. Something I would want to avoid. I'd use an `@ExceptionHandler` in an `@AroundAdvice` component instead – Stefan Haberl Jun 21 '19 at 07:36
  • If someone wants to use a preexisting exception that comes with Spring, `org.springframework.data.rest.webmvc.ResourceNotFoundException` already is annotated with `@ResponseStatus(HttpStatus.NOT_FOUND)` – George Pantazes May 12 '20 at 13:47
38

Your original method can return ResponseEntity (doesn't change your method behavior):

@RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public ResponseEntity getData(@PathVariable(ID_PARAMETER) long id, HttpServletResponse res{
... 
}

and return the following:

return new ResponseEntity(HttpStatus.NOT_FOUND);
dur
  • 15,689
  • 25
  • 79
  • 125
Amazia Gur
  • 1,692
  • 17
  • 16
  • 8
    To be precise, you should return ResponseEntity, where T is for example Response (first case in question). And you'll return new ResponseEntity(myResponse, HttpStatus.OK) when it is a correct response. – Vince Nov 28 '14 at 00:08
  • 1
    This way allows you to set headers as well, ResponseStatus doesn't, woot! – rogerdpack Jun 29 '18 at 16:43
  • 1
    Another good thing about this is it doesn't result in warnings in the log, which throwing the ResponseStatusException does. – GreenAsJade Sep 17 '19 at 03:16
  • But then you lose the type on the Response, which won't work well with Swagger generated documentation. – cs94njw Aug 20 '20 at 10:34
11

You can just set responseStatus on res like this:

@RequestMapping(value = VIDEO_DATA_PATH, method = RequestMethod.GET)
public ResponseEntity getData(@PathVariable(ID_PARAMETER) long id,
                                            HttpServletResponse res) {
...
    res.setStatus(HttpServletResponse.SC_NOT_FOUND); 
    // or res.setStatus(404)
    return null; // or build some response entity
 ...
}
Michał
  • 621
  • 5
  • 9