Spring Boot Rest Controller ¿cómo devolver diferentes códigos de estado HTTP?

102

Estoy usando Spring Boot para una API REST simple y me gustaría devolver un código de estado HTTP correcto si algo falla.

@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?
}

Siendo nuevo en Spring y Spring Boot, la pregunta básica es ¿cómo devuelvo códigos de estado diferentes cuando algo está bien o falla?

Marco
fuente

Respuestas:

117

Hay varias opciones que puede utilizar. Una buena manera es usar excepciones y clases para el manejo llamado @ControllerAdvice:

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

También puede pasar HttpServletResponseal método del controlador y simplemente establecer el código de respuesta:

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

Consulte esta excelente publicación de blog para obtener más detalles: Manejo de excepciones en Spring MVC


NOTA

En Spring MVC, el uso de la @ResponseBodyanotación es redundante, ya está incluido en la @RestControlleranotación.

Jakub Kubrynski
fuente
Como comentario, hice una prueba hace 15 minutos, y un '@RestController' sin la anotación '@ResponseBody' sobre su método colocó la cadena devuelta no dentro del cuerpo sino como ForderedURL. Soy bastante novato con spring / springboot por mí mismo, así que no puedo señalar por qué
Anearion
@Anearion Hay un error tipográfico en la respuesta: en realidad necesitamos '@RestControllerAdvice', no '@RestController'.
yoliho
No es un error tipográfico. Esta parte está relacionada con la pregunta y las anotaciones en un controlador
Jakub Kubrynski
1
Tenga en cuenta que javax.servlet.http.HttpServletResponseparece no tener todos los códigos de estado que los org.springframework.http.HttpStatustiene. Por lo tanto, puede usar HttpStatus.UNPROCESSABLE_ENTITY.value()para pasar el valor int a response.setStatus. Además, esto funciona perfectamente para el manejo de errores @ExceptionHandler.
Igor
43

Una de las formas de hacer esto es usar ResponseEntity como un 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
fuente
3
No es necesario usar el valor nulo en versiones posteriores de Spring: new ResponseEntity <> (HttpStatus.NOT_FOUND)
Kong
4

Prueba 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
fuente
Como esta es una pregunta bastante antigua, debe agregar la información del paquete y la versión a la que se refiere.
ZF007
¿Puede incluir una implementación de ejemplo de ErrorBean?
Brent Bradburn
3

Una buena forma es usar la ResponseStatusException de Spring

En lugar de devolver un ResponseEntityo similar, simplemente arroja el ResponseStatusExceptiondel controlador con una HttpStatuscausa y, por ejemplo:

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

o:

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

Esto da como resultado una respuesta al cliente que contiene el estado HTTP (por ejemplo, 400 Solicitud incorrecta) con un cuerpo como:

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

En caso de que desee devolver un código de estado personalizado definido, puede utilizar ResponseEntity como aquí:

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

CustomHttpStatusValue podría ser cualquier número entero dentro o fuera de los códigos de estado HTTP estándar.

Vedant Goenka
fuente
Me gusta este enfoque de API fluido.
v.ladynev
0

Hay diferentes formas de devolver el código de estado, 1: la clase RestController debe extender la clase BaseRest, en la clase BaseRest podemos manejar la excepción y devolver los códigos de error esperados. por ejemplo :

@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
fuente