Paul
Paul

Reputation: 20061

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

I have a custom exception class annotated to return a given HttpStatus:

@ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Invalid parameter")
public class BadRequestException extends RuntimeException
{
  public BadRequestException(String msg)
  {
    super(msg);
  }
}

This works when I throw a BadRequestException from my controller but the reason is always "Invalid parameter" of course. Is there a way to set the returned reason in this class? I'd like to pass a string to be used as the reason.

Thanks!

Upvotes: 41

Views: 24537

Answers (7)

Randy
Randy

Reputation: 809

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.

Upvotes: 23

helt
helt

Reputation: 5197

Since spring 5.0, you can use the ResponseStatusException which is available

// From https://www.baeldung.com/spring-response-status-exception
@GetMapping("/actor/{id}")
public String getActorName(@PathVariable("id") int id) {
    try {
        return actorService.getActor(id);
    } catch (ActorNotFoundException ex) {
        throw new ResponseStatusException(
          HttpStatus.NOT_FOUND, "Actor Not Found", ex);
    }
}

Upvotes: 7

Bozho
Bozho

Reputation: 597124

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");
}

Upvotes: 14

Mariusz Róg
Mariusz Róg

Reputation: 11

The easiest way to just set the response.setStatus(). Easy and clean, you can change it to any status you want just instead of ex.getStatusCode() add your code.

The return type is also of your choice, I'm using String b/c displaying this later.

By the way, the sendError is not a good idea, because JBoss for instance is adding a lot of HTML to the response.

@ExceptionHandler(CommunicationException.class)
@ResponseBody()
public String handleCommunicationException(CommunicationException ex, HttpServletResponse response) throws IOException{
    response.setStatus(ex.getStatusCode());
    return ex.getStatusMessage();   
}

Upvotes: 1

gertas
gertas

Reputation: 17145

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.

Upvotes: 12

Mansoor Siddiqui
Mansoor Siddiqui

Reputation: 21673

Annotations are meant to be static, and can't be set dynamically from your class. I suggest creating a subclass of your BadRequestException for every type of failure scenario and annotating them differently.

This doesn't just serve as a workaround -- if you're hiding the details regarding what went wrong in the reason message, then you're losing flexibility because any code that catches a BadRequestException will have to deal with all failure scenarios the same way.

Upvotes: 3

Rodrigo Camargo
Rodrigo Camargo

Reputation: 180

The "reason" is optional, so you can omit that and implements the abstract method public String reason passing the Error. Link: http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/web/bind/annotation/ResponseStatus.html

Upvotes: -3

Related Questions