How to respond with HTTP 400 error in a Spring MVC @ResponseBody method returning String?
Solution 1:
change your return type to ResponseEntity<>
, then you can use below for 400
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
and for correct request
return new ResponseEntity<>(json,HttpStatus.OK);
UPDATE 1
after spring 4.1 there are helper methods in ResponseEntity could be used as
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);
and
return ResponseEntity.ok(json);
Solution 2:
Something like this should work, I'm not sure whether or not there is a simpler way:
@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId, @RequestBody String body,
HttpServletRequest request, HttpServletResponse response) {
String json = matchService.getMatchJson(matchId);
if (json == null) {
response.setStatus( HttpServletResponse.SC_BAD_REQUEST );
}
return json;
}
Solution 3:
Not necessarily the most compact way of doing this, but quite clean IMO
if(json == null) {
throw new BadThingException();
}
...
@ExceptionHandler(BadThingException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public @ResponseBody MyError handleException(BadThingException e) {
return new MyError("That doesnt work");
}
Edit you can use @ResponseBody in the exception handler method if using Spring 3.1+, otherwise use a ModelAndView
or something.
https://jira.springsource.org/browse/SPR-6902
Solution 4:
I would change the implementation slightly:
First, I create a UnknownMatchException
:
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UnknownMatchException extends RuntimeException {
public UnknownMatchException(String matchId) {
super("Unknown match: " + matchId);
}
}
Note the use of @ResponseStatus, which will be recognized by Spring's ResponseStatusExceptionResolver
. If the exception is thrown, it will create a response with the corresponding response status. (I also took the liberty of changing the status code to 404 - Not Found
which I find more appropriate for this use case, but you can stick to HttpStatus.BAD_REQUEST
if you like.)
Next, I would change the MatchService
to have the following signature:
interface MatchService {
public Match findMatch(String matchId);
}
Finally, I would update the controller and delegate to Spring's MappingJackson2HttpMessageConverter
to handle the JSON serialization automatically (it is added by default if you add Jackson to the classpath and add either @EnableWebMvc
or <mvc:annotation-driven />
to your config, see the reference docs):
@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Match match(@PathVariable String matchId) {
// throws an UnknownMatchException if the matchId is not known
return matchService.findMatch(matchId);
}
Note, it is very common to separate the domain objects from the view objects or DTO objects. This can easily be achieved by adding a small DTO factory that returns the serializable JSON object:
@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public MatchDTO match(@PathVariable String matchId) {
Match match = matchService.findMatch(matchId);
return MatchDtoFactory.createDTO(match);
}
Solution 5:
Here's a different approach. Create a custom Exception
annotated with @ResponseStatus
, like the following one.
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Not Found")
public class NotFoundException extends Exception {
public NotFoundException() {
}
}
And throw it when needed.
@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
String json = matchService.getMatchJson(matchId);
if (json == null) {
throw new NotFoundException();
}
return json;
}
Check out the Spring documentation here: http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#mvc-ann-annotated-exceptions.