Es gibt mehrere Anwendungsfälle zum Festlegen von HTTP-Statuscodes in einem REST-Webdienst, und mindestens einer wurde in den vorhandenen Antworten nicht ausreichend dokumentiert (z. B. wenn Sie die automatische magische JSON / XML-Serialisierung mit JAXB verwenden und einen zurückgeben möchten zu serialisierendes Objekt, aber auch ein anderer Statuscode als der Standard 200).
Lassen Sie mich versuchen, die verschiedenen Anwendungsfälle und Lösungen für jeden einzelnen aufzuzählen:
1. Fehlercode (500, 404, ...)
Der häufigste Anwendungsfall, wenn Sie einen anderen Statuscode als 200 OK
beim Auftreten eines Fehlers zurückgeben möchten .
Beispielsweise:
- Eine Entität wird angefordert, existiert aber nicht (404)
- Die Anfrage ist semantisch falsch (400)
- der Benutzer ist nicht autorisiert (401)
- Es liegt ein Problem mit der Datenbankverbindung vor (500).
- etc..
a) Wirf eine Ausnahme aus
In diesem Fall denke ich, dass der sauberste Weg, um das Problem zu lösen, darin besteht, eine Ausnahme auszulösen. Diese Ausnahme wird von einem behandeltExceptionMapper
, der die Ausnahme in eine Antwort mit dem entsprechenden Fehlercode übersetzt.
Sie können die ExceptionMapper
mit Jersey vorkonfigurierte Standardeinstellung verwenden (und ich denke, dass dies auch bei anderen Implementierungen der Fall ist) und eine der vorhandenen Unterklassen von werfen javax.ws.rs.WebApplicationException
. Hierbei handelt es sich um vordefinierte Ausnahmetypen, die verschiedenen Fehlercodes vorab zugeordnet sind, z. B.:
- BadRequestException (400)
- InternalServerErrorException (500)
- NotFoundException (404)
Die Liste finden Sie hier: API
Alternativ können Sie Ihre eigenen benutzerdefinierten Ausnahmen und ExceptionMapper
Klassen definieren und diese Zuordnungen mithilfe der @Provider
Anmerkung ( Quelle dieses Beispiels ) zu Jersey hinzufügen :
public class MyApplicationException extends Exception implements Serializable
{
private static final long serialVersionUID = 1L;
public MyApplicationException() {
super();
}
public MyApplicationException(String msg) {
super(msg);
}
public MyApplicationException(String msg, Exception e) {
super(msg, e);
}
}
Anbieter :
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Hinweis: Sie können auch ExceptionMappers für vorhandene Ausnahmetypen schreiben, die Sie verwenden.
b) Verwenden Sie den Response Builder
Eine andere Möglichkeit, einen Statuscode festzulegen, besteht darin, einen Response
Builder zu verwenden, um eine Antwort mit dem beabsichtigten Code zu erstellen.
In diesem Fall muss der Rückgabetyp Ihrer Methode sein javax.ws.rs.core.Response
. Dies wird in verschiedenen anderen Antworten beschrieben, beispielsweise in der von hisdrewness akzeptierten Antwort, und sieht folgendermaßen aus:
@GET
@Path("myresource({id}")
public Response retrieveSomething(@PathParam("id") String id) {
...
Entity entity = service.getById(uuid);
if(entity == null) {
return Response.status(Response.Status.NOT_FOUND).entity("Resource not found for ID: " + uuid).build();
}
...
}
2. Erfolg, aber nicht 200
Ein anderer Fall, in dem Sie den Rückgabestatus festlegen möchten, ist, wenn der Vorgang erfolgreich war, Sie jedoch einen anderen Erfolgscode als 200 zusammen mit dem Inhalt zurückgeben möchten, den Sie im Hauptteil zurückgeben.
Ein häufiger Anwendungsfall ist, wenn Sie eine neue Entität ( POST
Anforderung) erstellen und Informationen zu dieser neuen Entität oder möglicherweise zur Entität selbst zusammen mit einem 201 Created
Statuscode zurückgeben möchten .
Ein Ansatz besteht darin, das Antwortobjekt wie oben beschrieben zu verwenden und den Hauptteil der Anforderung selbst festzulegen. Auf diese Weise verlieren Sie jedoch die Möglichkeit, die von JAXB bereitgestellte automatische Serialisierung in XML oder JSON zu verwenden.
Dies ist die ursprüngliche Methode, die ein Entitätsobjekt zurückgibt, das von JAXB an JSON serialisiert wird:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
Dadurch wird eine JSON-Darstellung des neu erstellten Benutzers zurückgegeben, der Rückgabestatus lautet jedoch 200 und nicht 201.
Das Problem ist nun, dass ich Response
ein Response
Objekt in meiner Methode zurückgeben muss, wenn ich den Builder zum Festlegen des Rückkehrcodes verwenden möchte . Wie kann ich das User
zu serialisierende Objekt trotzdem zurückgeben ?
a) Stellen Sie den Code in der Servlet-Antwort ein
Ein Ansatz, um dies zu lösen, besteht darin, ein Servlet-Anforderungsobjekt zu erhalten und den Antwortcode manuell selbst festzulegen, wie in Garett Wilsons Antwort gezeigt:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user, @Context final HttpServletResponse response){
User newUser = ...
//set HTTP code to "201 Created"
response.setStatus(HttpServletResponse.SC_CREATED);
try {
response.flushBuffer();
}catch(Exception e){}
return newUser;
}
Die Methode gibt weiterhin ein Entitätsobjekt zurück und der Statuscode lautet 201.
Beachten Sie, dass ich die Antwort leeren musste, damit es funktioniert. Dies ist eine unangenehme Wiederbelebung des Servlet-API-Codes auf niedriger Ebene in unserer netten JAX_RS-Ressource. Schlimmer noch, die Header können danach nicht mehr geändert werden, da sie bereits über die Leitung gesendet wurden.
b) Verwenden Sie das Antwortobjekt mit der Entität
In diesem Fall besteht die beste Lösung darin, das Antwortobjekt zu verwenden und die zu serialisierende Entität für dieses Antwortobjekt festzulegen. Es wäre schön, das Response-Objekt generisch zu machen, um den Typ der Nutzdatenentität in diesem Fall anzugeben, dies ist jedoch derzeit nicht der Fall.
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public Response addUser(User user){
User newUser = ...
return Response.created(hateoas.buildLinkUri(newUser, "entity")).entity(restResponse).build();
}
In diesem Fall verwenden wir die erstellte Methode der Response Builder-Klasse, um den Statuscode auf 201 zu setzen. Wir übergeben das Entitätsobjekt (Benutzer) über die entity () -Methode an die Antwort.
Das Ergebnis ist, dass der HTTP-Code wie gewünscht 401 lautet und der Hauptteil der Antwort genau derselbe JSON ist, den wir zuvor hatten, als wir gerade das User-Objekt zurückgegeben haben. Außerdem wird ein Standortheader hinzugefügt.
Die Response-Klasse verfügt über eine Reihe von Builder-Methoden für verschiedene Status (stati?), Wie z.
Response.accepted () Response.ok () Response.noContent () Response.notAcceptable ()
NB: Das Hateoas-Objekt ist eine Hilfsklasse, die ich entwickelt habe, um Ressourcen-URIs zu generieren. Sie müssen hier Ihren eigenen Mechanismus entwickeln;)
Das ist alles.
Ich hoffe diese lange Antwort hilft jemandem :)