Spring Boot Rest Controller como retornar diferentes códigos de status HTTP?

102

Estou usando o Spring Boot para uma API REST simples e gostaria de retornar um código de status HTTP correto se algo falhar.

@RequestMapping(value="/rawdata/", method = RequestMethod.PUT)
@ResponseBody
@ResponseStatus( HttpStatus.OK )
public RestModel create(@RequestBody String data) {
    // code ommitted..
    // how do i return a correct status code if something fails?
}

Sendo novo no Spring e no Spring Boot, a questão básica é como retorno códigos de status diferentes quando algo está ok ou falha?

Marco
fonte

Respostas:

117

Existem várias opções que você pode usar. Uma maneira bastante boa é usar exceções e classes para lidar com chamadas @ControllerAdvice:

@ControllerAdvice
class GlobalControllerExceptionHandler {
    @ResponseStatus(HttpStatus.CONFLICT)  // 409
    @ExceptionHandler(DataIntegrityViolationException.class)
    public void handleConflict() {
        // Nothing to do
    }
}

Além disso, você pode passar HttpServletResponsepara o método do controlador e apenas definir o código de resposta:

public RestModel create(@RequestBody String data, HttpServletResponse response) {
    // response committed...
    response.setStatus(HttpServletResponse.SC_ACCEPTED);
}

Consulte esta excelente postagem no blog para obter detalhes: Tratamento de exceções no Spring MVC


NOTA

No Spring MVC, o uso de @ResponseBodyanotação é redundante - já está incluído na @RestControlleranotação.

Jakub Kubrynski
fonte
Apenas como comentário, eu fiz um teste 15 minutos atrás, e um '@RestController' sem a anotação '@ResponseBody' sobre seu método colocou a string retornada não dentro do corpo, mas como ForwardedURL. Eu sou muito novato com spring / springboot, então não posso apontar o porquê
Anearion
@Anearion Há um erro de digitação na resposta - na verdade precisamos de '@RestControllerAdvice', não de '@RestController'.
yoliho de
Não é um erro de digitação. Esta parte está relacionada à pergunta e anotações em um controlador
Jakub Kubrynski
1
Observe que javax.servlet.http.HttpServletResponseparece não ter todos os StatusCodes que org.springframework.http.HttpStatustem. Portanto, você pode usar HttpStatus.UNPROCESSABLE_ENTITY.value()para passar o valor int para response.setStatus. Também funciona perfeitamente para o tratamento de erros usando @ExceptionHandler.
Igor
43

Uma das maneiras de fazer isso é usar ResponseEntity como um objeto de retorno.

@RequestMapping(value="/rawdata/", method = RequestMethod.PUT)

public ResponseEntity<?> create(@RequestBody String data) {

if(everything_fine)
    return new ResponseEntity<>(RestModel, HttpStatus.OK);
else
    return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);

}
Ankit Basarkar
fonte
3
Não há necessidade de usar o nulo em versões posteriores do Spring: new ResponseEntity <> (HttpStatus.NOT_FOUND)
Kong,
4

Experimente este código:

@RequestMapping(value = "/validate", method = RequestMethod.GET, produces = "application/json")
public ResponseEntity<ErrorBean> validateUser(@QueryParam("jsonInput") final String jsonInput) {
    int numberHTTPDesired = 400;
    ErrorBean responseBean = new ErrorBean();
    responseBean.setError("ERROR");
    responseBean.setMensaje("Error in validation!");

    return new ResponseEntity<ErrorBean>(responseBean, HttpStatus.valueOf(numberHTTPDesired));
}
Rodrigo Hernandez
fonte
Como esta é uma questão bastante antiga, você deve adicionar informações sobre o pacote e a versão a que se refere.
ZF007
Você pode incluir um exemplo de implementação de ErrorBean?
Brent Bradburn
3

Uma boa maneira é usar ResponseStatusException do Spring

Em vez de retornar um ResponseEntityou semelhante, você simplesmente joga o ResponseStatusExceptiondo controlador com um HttpStatuse causa, por exemplo:

throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Cause description here");

ou:

throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Cause description here");

Isso resulta em uma resposta ao cliente contendo o status HTTP (por exemplo, 400 Bad request) com um corpo como:

{
  "timestamp": "2020-07-09T04:43:04.695+0000",
  "status": 400,
  "error": "Bad Request",
  "message": "Cause description here",
  "path": "/test-api/v1/search"
}
Continuidade 8
fonte
2

Caso queira retornar um código de status definido de forma personalizada, você pode usar o ResponseEntity como aqui:

@RequestMapping(value="/rawdata/", method = RequestMethod.PUT)
public ResponseEntity<?> create(@RequestBody String data) {
    int customHttpStatusValue = 499;
    Foo foo = bar();
    return ResponseEntity.status(customHttpStatusValue).body(foo);
}

O CustomHttpStatusValue pode ser qualquer número inteiro dentro ou fora dos códigos de status HTTP padrão.

Vedant Goenka
fonte
Eu gosto dessa abordagem de API fluente.
v.ladynev
0

Existem diferentes maneiras de retornar o código de status, 1: a classe RestController deve estender a classe BaseRest, na classe BaseRest podemos tratar a exceção e retornar os códigos de erro esperados. por exemplo :

@RestController
@RequestMapping
class RestController extends BaseRest{

}

@ControllerAdvice
public class BaseRest {
@ExceptionHandler({Exception.class,...})
    @ResponseStatus(value=HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorModel genericError(HttpServletRequest request, 
            HttpServletResponse response, Exception exception) {
        
        ErrorModel error = new ErrorModel();
        resource.addError("error code", exception.getLocalizedMessage());
        return error;
    }
Manju D
fonte