Esistono diversi casi d'uso per l'impostazione dei codici di stato HTTP in un servizio Web REST e almeno uno non è stato sufficientemente documentato nelle risposte esistenti (ad esempio quando si utilizza la serializzazione JSON / XML auto-magica utilizzando JAXB e si desidera restituire un oggetto da serializzare, ma anche un codice di stato diverso dal 200 predefinito).
Vorrei quindi provare a elencare i diversi casi d'uso e le soluzioni per ognuno:
1. Codice errore (500, 404, ...)
Il caso d'uso più comune quando si desidera restituire un codice di stato diverso da 200 OK
quando si verifica un errore.
Per esempio:
- è richiesta un'entità ma non esiste (404)
- la richiesta è semanticamente errata (400)
- l'utente non è autorizzato (401)
- c'è un problema con la connessione al database (500)
- eccetera..
a) Generare un'eccezione
In tal caso, penso che il modo più pulito per gestire il problema sia quello di generare un'eccezione. Questa eccezione verrà gestita da un ExceptionMapper
, che tradurrà l'eccezione in una risposta con il codice di errore appropriato.
È possibile utilizzare il valore predefinito ExceptionMapper
preconfigurato con Jersey (e immagino che sia lo stesso con altre implementazioni) e lanciare una delle sottoclassi esistenti di javax.ws.rs.WebApplicationException
. Si tratta di tipi di eccezione predefiniti che sono pre-mappati su diversi codici di errore, ad esempio:
- BadRequestException (400)
- InternalServerErrorException (500)
- NotFoundException (404)
Ecc. Puoi trovare l'elenco qui: API
In alternativa, puoi definire le tue eccezioni e ExceptionMapper
classi personalizzate e aggiungere questi mappatori a Jersey in base @Provider
all'annotazione ( fonte di questo esempio ):
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);
}
}
Fornitore:
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Nota: puoi anche scrivere ExceptionMapper per i tipi di eccezione esistenti che usi.
b) Utilizzare il builder Response
Un altro modo per impostare un codice di stato è utilizzare un Response
builder per creare una risposta con il codice previsto.
In tal caso, il tipo restituito dal metodo deve essere javax.ws.rs.core.Response
. Questo è descritto in varie altre risposte come la risposta accettata da Hisdrewness e si presenta così:
@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. Successo, ma non 200
Un altro caso in cui si desidera impostare lo stato di ritorno è quando l'operazione ha avuto esito positivo, ma si desidera restituire un codice di successo diverso da 200, insieme al contenuto che si restituisce nel corpo.
Un caso di utilizzo frequente è quando si crea una nuova entità ( POST
richiesta) e si desidera restituire informazioni su questa nuova entità o sull'entità stessa, insieme a un 201 Created
codice di stato.
Un approccio consiste nell'utilizzare l'oggetto response proprio come descritto sopra e impostare tu stesso il corpo della richiesta. Tuttavia, perdendo la possibilità di utilizzare la serializzazione automatica in XML o JSON fornita da JAXB.
Questo è il metodo originale che restituisce un oggetto entità che verrà serializzato su JSON da JAXB:
@Path("/")
@POST
@Consumes({ MediaType.APPLICATION_JSON })
@Produces({ MediaType.APPLICATION_JSON })
public User addUser(User user){
User newuser = ... do something like DB insert ...
return newuser;
}
Ciò restituirà una rappresentazione JSON dell'utente appena creato, ma lo stato di ritorno sarà 200, non 201.
Ora il problema è se voglio usare il Response
builder per impostare il codice di ritorno, devo restituire un Response
oggetto nel mio metodo. Come posso ancora restituire l' User
oggetto da serializzare?
a) Impostare il codice sulla risposta servlet
Un approccio per risolvere questo problema è ottenere un oggetto richiesta servlet e impostare manualmente il codice di risposta, come dimostrato nella risposta di Garett Wilson:
@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;
}
Il metodo restituisce comunque un oggetto entità e il codice di stato sarà 201.
Nota che per farlo funzionare, ho dovuto svuotare la risposta. Questa è una spiacevole rinascita del codice API Servlet di basso livello nella nostra bella risorsa JAX_RS, e molto peggio, fa sì che le intestazioni non siano modificabili dopo questo perché erano già state inviate sul filo.
b) Utilizzare l'oggetto risposta con l'entità
La soluzione migliore, in tal caso, è utilizzare l'oggetto Response e impostare l'entità da serializzare su questo oggetto response. Sarebbe bello rendere generico l'oggetto Response per indicare il tipo di entità payload in quel caso, ma non è attualmente il caso.
@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 tal caso, utilizziamo il metodo creato della classe Builder Response per impostare il codice di stato su 201. Passiamo l'oggetto entità (utente) alla risposta tramite il metodo entity ().
Il risultato è che il codice HTTP è 401 come volevamo, e il corpo della risposta è esattamente lo stesso JSON che avevamo prima quando abbiamo appena restituito l'oggetto Utente. Aggiunge anche un'intestazione di posizione.
La classe Response ha un numero di metodo builder per diversi stati (stati?) Come:
Response.accepted () Response.ok () Response.noContent () Response.notAcceptable ()
NB: l'oggetto hateoas è una classe di supporto che ho sviluppato per aiutare a generare risorse URI. Dovrai inventare il tuo meccanismo qui;)
Questo è tutto.
Spero che questa lunga risposta aiuti qualcuno :)