451 Stimmen

Wie reagiert man in einer Spring MVC @ResponseBody-Methode, die einen String zurückgibt, mit einem HTTP 400-Fehler?

Ich verwende Spring MVC für eine einfache JSON-API, mit einem @ResponseBody Ansatz wie folgt. (Ich habe bereits eine Service-Schicht, die JSON direkt erstellt.)

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        // TODO: Wie kann man z.B. mit 400 "Bad Request" antworten?
    }
    return json;
}

In dem gegebenen Szenario, was ist der einfachste, sauberste Weg, um mit einem HTTP 400 Fehler zu antworten?

Ich bin auf Ansätze wie die folgenden gestoßen:

return new ResponseEntity(HttpStatus.BAD_REQUEST);

...aber ich kann es hier nicht verwenden, da der Rückgabetyp meiner Methode String ist, nicht ResponseEntity.

710voto

Bassem Reda Zohdy Punkte 12387

Ändern Sie Ihren Rückgabetyp in ResponseEntity, und dann können Sie das Folgende für 400 verwenden:

return new ResponseEntity<>(HttpStatus.BAD_REQUEST);

Und für eine korrekte Anfrage:

return new ResponseEntity<>(json,HttpStatus.OK);

Nach Spring 4.1 gibt es Hilfsmethoden in ResponseEntity, die wie folgt verwendet werden können:

return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(null);

und

return ResponseEntity.ok(json);

116voto

stacker Punkte 65961

Etwas in dieser Art sollte funktionieren, aber ich bin mir nicht sicher, ob es einen einfacheren Weg gibt:

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId, @RequestBody String body,
            HttpServletRequest request, HttpServletResponse response) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        response.setStatus( HttpServletResponse.SC_BAD_REQUEST  );
    }
    return json;
}

57voto

Zutty Punkte 5339

Es ist zwar nicht unbedingt der kompakteste Weg, dies zu tun, aber meiner Meinung nach ziemlich sauber:

if(json == null) {
    throw new BadThingException();
}
...

@ExceptionHandler(BadThingException.class)
@ResponseStatus(value = HttpStatus.BAD_REQUEST)
public @ResponseBody MyError handleException(BadThingException e) {
    return new MyError("Das funktioniert nicht");
}

Sie können @ResponseBody in der Ausnahmehandler-Methode verwenden, wenn Sie Spring 3.1+ verwenden, verwenden Sie anderenfalls ein ModelAndView oder Ähnliches.

@ResponseBody does not work with @ExceptionHandler [SPR-6902] #11567

51voto

matsev Punkte 29106

Ich würde die Implementierung leicht ändern:

Zuerst erstelle ich eine UnknownMatchException:

@ResponseStatus(HttpStatus.NOT_FOUND)
public class UnknownMatchException extends RuntimeException {
    public UnknownMatchException(String matchId) {
        super("Unbekanntes Match: " + matchId);
    }
}

Beachten Sie die Verwendung von @ResponseStatus, das vom ResponseStatusExceptionResolver von Spring erkannt wird. Wenn die Ausnahme ausgelöst wird, wird eine Antwort mit dem entsprechenden Antwortstatus erstellt. (Ich habe auch die Freiheit genommen, den Statuscode auf 404 - Not Found zu ändern, was ich für diesen Anwendungsfall angemessener finde, aber Sie können sich auch für HttpStatus.BAD_REQUEST entscheiden, wenn Sie möchten.)


Dann würde ich die Signatur des MatchService ändern, um folgendes zu haben:

Schnittstelle MatchService {
    public Match findMatch(String matchId);
}

Zuletzt würde ich den Controller aktualisieren und Spring's MappingJackson2HttpMessageConverter verwenden, um die JSON-Serialisierung automatisch zu handhaben (es wird standardmäßig hinzugefügt, wenn Sie Jackson zum Klassenpfad hinzufügen und entweder @EnableWebMvc oder zu Ihrer Konfiguration hinzufügen. Siehe die Referenzdokumentation):

@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public Match match(@PathVariable String matchId) {
    // Wirft eine UnknownMatchException, wenn die matchId nicht bekannt ist
    return matchService.findMatch(matchId);
}

Bemerkenswert ist, dass es sehr üblich ist, die Domänenobjekte von den View-Objekten oder DTO-Objekten zu trennen. Dies kann leicht durch Hinzufügen einer kleinen DTO-Fabrik erreicht werden, die das serialisierbare JSON-Objekt zurückgibt:

@RequestMapping(value = "/matches/{matchId}", produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public MatchDTO match(@PathVariable String matchId) {
    Match match = matchService.findMatch(matchId);
    return MatchDtoFactory.createDTO(match);
}

40voto

danidemi Punkte 3854

Hier ist ein anderer Ansatz. Erstellen Sie eine benutzerdefinierte Ausnahme, die mit @ResponseStatus annotiert ist, wie die folgende.

@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Not Found")
public class NotFoundException extends Exception {

    public NotFoundException() {
    }
}

Und werfen Sie es bei Bedarf.

@RequestMapping(value = "/matches/{matchId}", produces = "application/json")
@ResponseBody
public String match(@PathVariable String matchId) {
    String json = matchService.getMatchJson(matchId);
    if (json == null) {
        throw new NotFoundException();
    }
    return json;
}

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X