Spring Boot Rest Controller jak zwrócić różne kody stanu HTTP?

102

Używam Spring Boot do prostego interfejsu API REST i chciałbym zwrócić poprawny kod stanu HTTP, jeśli coś się nie powiedzie.

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

Będąc nowicjuszem w Spring i Spring Boot, podstawowe pytanie brzmi: jak zwrócić różne kody stanu, gdy coś jest w porządku lub nie działa?

Marco
źródło

Odpowiedzi:

118

Istnieje kilka opcji, z których możesz skorzystać. Całkiem dobrym sposobem jest użycie wyjątków i klasy do obsługi o nazwie @ControllerAdvice:

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

Możesz również przejść HttpServletResponsedo metody kontrolera i po prostu ustawić kod odpowiedzi:

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

Szczegółowe informacje można znaleźć w tym wspaniałym wpisie na blogu: Obsługa wyjątków w Spring MVC


UWAGA

W Spring MVC używanie @ResponseBodyadnotacji jest zbędne - jest już zawarte w @RestControlleradnotacji.

Jakub Kubryński
źródło
Tak jak komentarz, zrobiłem test 15 minut temu, a „@RestController” bez adnotacji „@ResponseBody” nad jego metodą umieścił ciąg zwrócony nie wewnątrz treści, ale jako ForwardedURL. Jestem całkiem noobem ze sprężynami /
butami
@Anearion W odpowiedzi jest literówka - tak naprawdę potrzebujemy „@RestControllerAdvice”, a nie „@RestController”.
yoliho
To nie jest literówka. Ta część dotyczy pytania i adnotacji na kontrolerze
Jakub Kubryński
1
Zauważ, że javax.servlet.http.HttpServletResponsewydaje się , że nie ma wszystkich kodów statusu, które mają org.springframework.http.HttpStatus. Możesz więc użyć HttpStatus.UNPROCESSABLE_ENTITY.value()do przekazania wartości int do response.setStatus. Również to doskonale sprawdza się w przypadku obsługi błędów przy użyciu @ExceptionHandler.
Igor
43

Jednym ze sposobów jest użycie ResponseEntity jako obiektu zwracanego.

@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
źródło
3
Nie ma potrzeby używania wartości null w nowszych wersjach Spring: new ResponseEntity <> (HttpStatus.NOT_FOUND)
Kong
4

Wypróbuj ten kod:

@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
źródło
Ponieważ jest to dość stare pytanie, powinieneś dodać informacje o pakiecie i wersji, do których się odnosisz.
ZF007
Czy możesz dołączyć przykładową implementację ErrorBean?
Brent Bradburn
4

Przyjemnym sposobem jest użycie wyjątku ResponseStatusException Springa

Zamiast zwracać a ResponseEntitylub coś podobnego, po prostu wyrzucasz ResponseStatusExceptionze sterownika z HttpStatusprzyczyną i, na przykład:

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

lub:

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

Skutkuje to odpowiedzią do klienta zawierającą status HTTP (np. 400 Bad request) z treścią taką jak:

{
  "timestamp": "2020-07-09T04:43:04.695+0000",
  "status": 400,
  "error": "Bad Request",
  "message": "Cause description here",
  "path": "/test-api/v1/search"
}
Ciągłość 8
źródło
Proste, czyste i możliwe do wykorzystania przez klienta REST.
Alecz
2

Jeśli chcesz zwrócić niestandardowy kod stanu, możesz użyć elementu ResponseEntity, jak tutaj:

@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 może być dowolną liczbą całkowitą w ramach standardowych kodów stanu HTTP lub poza nimi.

Vedant Goenka
źródło
Podoba mi się to płynne podejście do API.
przeciwko Ladynev
0

Istnieją różne sposoby zwracania kodu statusu, 1: klasa RestController powinna rozszerzać klasę BaseRest, w klasie BaseRest możemy obsłużyć wyjątek i zwrócić oczekiwane kody błędów. na przykład :

@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
źródło