In Spring 3 is it possible to dynamically set the reason of @ResponseStatus?

You can use HttpServletResponse's sendError function to achieve that.
Here is an example of how to use it:

@RequestMapping(value = "some/url", method = RequestMethod.POST)
public void doAction(final HttpServletResponse response) throws IOException {
  response.sendError(HttpStatus.BAD_REQUEST.value(), "custom error message");
}

The correct way is to introduce exception handler in your controller, then you can set response body of any status code:

@Controller
@RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public class SomeController {
...
  @ExceptionHandler(BadRequestException.class)
  @ResponseStatus(HttpStatus.BAD_REQUEST)
  public @ResponseBody
   Map<String,Object> handleIndexNotFoundException(BadRequestException bre,
                                           HttpServletRequest request, HttpServletResponse resp) {
     HashMap<String, Object> result = new HashMap<>();
     result.put("error", true);
     result.put("error_message", bre.getMessage());
     return result;
  }
}

Move over you don't have to pollute your model/exception classes with any Spring Web MVC annotations and dependency.

If you want to share the handler with all controller look into @ControllerAdvice.


If you omit the 'reason' attribute in the @ResponseStatus annotation on a custom exception,

@ResponseStatus(value = HttpStatus.CONFLICT)  // 409
public class ChildDataExists extends RuntimeException {
...

then throw the exception - in your service layer. Thus you don't need a catch and throw something else or catch in the controller to set the response directly to some HTTP status code.

throw new ChildDataExists("Can't delete parent if child row exists.");

The exception's message comes through as the 'message' of the 'data' in the JSON output. It seems the 'reason' in the annotation overrides the custom behavior. So you can have say one basic exception for a given context and use it in a dozen places, each with a slightly differing message where it is thrown and all get's handled properly out to the REST interface.