Spring Boot Rest Controller come restituire diversi codici di stato HTTP?


102

Sto usando Spring Boot per una semplice API REST e vorrei restituire uno stato HTTP corretto se qualcosa non funziona.

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

Essendo nuovo in Spring e Spring Boot, la domanda di base è come restituire codici di stato diversi quando qualcosa va bene o non funziona?

Risposte:


117

Ci sono diverse opzioni che puoi usare. Un modo abbastanza buono è usare eccezioni e classi per la gestione di chiamate @ControllerAdvice:

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

Inoltre puoi passare HttpServletResponseal metodo del controller e impostare semplicemente il codice di risposta:

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

Fare riferimento a questo fantastico post del blog per i dettagli: Gestione delle eccezioni in Spring MVC


NOTA

In Spring MVC l'uso @ResponseBodydell'annotazione è ridondante: è già incluso nell'annotazione @RestController.


Proprio come commento, ho fatto un test 15 minuti fa e un "@RestController" senza l'annotazione "@ResponseBody" sul suo metodo ha posizionato la stringa restituita non all'interno del corpo ma come ForwardedURL. Sono piuttosto noob con la primavera / primavera, quindi non posso spiegare perché
Anearion,

@Anearion C'è un errore di battitura nella risposta: in realtà abbiamo bisogno di "@RestControllerAdvice", non di "@RestController".
yoliho

Non è un errore di battitura. Questa parte è correlata alla domanda e alle annotazioni su un controller
Jakub Kubrynski

1
Nota, javax.servlet.http.HttpServletResponsesembra che non abbia tutti gli StatusCodes che ne hanno org.springframework.http.HttpStatus. Quindi puoi usare HttpStatus.UNPROCESSABLE_ENTITY.value()per passare il valore int a response.setStatus. Anche questo funziona perfettamente per la gestione degli errori utilizzando @ExceptionHandler.
Igor

43

Uno dei modi per farlo è usare ResponseEntity come oggetto di ritorno.

@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);

}

3
Non è necessario utilizzare il valore null nelle versioni successive di Spring: new ResponseEntity <> (HttpStatus.NOT_FOUND)
Kong

4

Prova questo codice:

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

Poiché questa è una domanda piuttosto vecchia, dovresti aggiungere le informazioni sul pacchetto e sulla versione a cui fai riferimento.
ZF007

Puoi includere un esempio di implementazione di ErrorBean?
Brent Bradburn

3

Un bel modo è usare ResponseStatusException di Spring

Piuttosto che restituire un ResponseEntityo simile, devi semplicemente lanciare il ResponseStatusExceptiondal controller con una HttpStatuscausa e, ad esempio:

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

o:

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

Ciò si traduce in una risposta al client contenente lo stato HTTP (ad esempio 400 Bad request) con un corpo come:

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

2

Nel caso in cui desideri restituire un codice di stato definito personalizzato, puoi utilizzare ResponseEntity come qui:

@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 può essere qualsiasi numero intero all'interno o all'esterno dei codici di stato HTTP standard.


Mi piace questo approccio API fluente.
v.ladynev

0

Esistono diversi modi per restituire il codice di stato, 1: la classe RestController dovrebbe estendere la classe BaseRest, nella classe BaseRest possiamo gestire le eccezioni e restituire i codici di errore previsti. per esempio :

@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;
    }
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.