Istnieje kilka przypadków użycia do ustawiania kodów stanu HTTP w usłudze sieciowej REST, a co najmniej jeden nie został wystarczająco udokumentowany w istniejących odpowiedziach (tj. Gdy używasz auto-magicznej serializacji JSON / XML za pomocą JAXB, a chcesz zwrócić obiekt do serializacji, ale także kod stanu inny niż domyślny 200).
Pozwólcie, że spróbuję wyliczyć różne przypadki użycia i rozwiązania dla każdego z nich:
1. Kod błędu (500, 404, ...)
Najczęstszym przypadkiem użycia, gdy chcesz zwrócić kod stanu inny niż w 200 OK
przypadku wystąpienia błędu.
Na przykład:
- jednostka jest proszona, ale nie istnieje (404)
- żądanie jest semantycznie nieprawidłowe (400)
- użytkownik nie ma uprawnień (401)
- wystąpił problem z połączeniem z bazą danych (500)
- itp..
a) Rzuć wyjątek
W takim przypadku uważam, że najczystszym sposobem rozwiązania problemu jest zgłoszenie wyjątku. Ten wyjątek zostanie obsłużony przez ExceptionMapper
, który przełoży wyjątek na odpowiedź z odpowiednim kodem błędu.
Możesz użyć domyślnej, ExceptionMapper
która jest wstępnie skonfigurowana z Jersey (i myślę, że to samo z innymi implementacjami) i rzucić dowolną z istniejących podklas javax.ws.rs.WebApplicationException
. Są to wstępnie zdefiniowane typy wyjątków, które są wstępnie mapowane na różne kody błędów, na przykład:
- BadRequestException (400)
- InternalServerErrorException (500)
- NotFoundException (404)
Itd. Możesz znaleźć listę tutaj: API
Alternatywnie możesz zdefiniować własne wyjątki i ExceptionMapper
klasy oraz dodać tych twórców map do Jersey za pomocą @Provider
adnotacji ( źródło tego przykładu ):
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);
}
}
Dostawca:
@Provider
public class MyApplicationExceptionHandler implements ExceptionMapper<MyApplicationException>
{
@Override
public Response toResponse(MyApplicationException exception)
{
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
Uwaga: możesz także napisać ExceptionMappers dla istniejących typów wyjątków, których używasz.
b) Użyj konstruktora odpowiedzi
Innym sposobem ustawienia kodu stanu jest użycie Response
konstruktora do zbudowania odpowiedzi za pomocą zamierzonego kodu.
W takim przypadku typem zwracanym przez metodę musi być javax.ws.rs.core.Response
. Jest to opisane w różnych innych odpowiedziach, takich jak zaakceptowana odpowiedź hisdrewness i wygląda następująco:
@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. Sukces, ale nie 200
Innym przypadkiem, w którym chcesz ustawić status zwrotu, jest operacja zakończona powodzeniem, ale chcesz zwrócić kod powodzenia inny niż 200 wraz z treścią zwracaną w treści.
Częstym przypadkiem jest sytuacja, gdy tworzysz nową jednostkę ( POST
żądanie) i chcesz zwrócić informacje o tej nowej jednostce lub może samej jednostce, wraz z 201 Created
kodem statusu.
Jednym z podejść jest użycie obiektu odpowiedzi tak jak opisano powyżej i samodzielne ustawienie treści żądania. Jednak w ten sposób tracisz możliwość korzystania z automatycznej serializacji do XML lub JSON zapewnianej przez JAXB.
To jest oryginalna metoda zwracająca obiekt encji, który zostanie zserializowany do JSON przez 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;
}
Zwróci to reprezentację JSON nowo utworzonego użytkownika, ale zwróci status 200, a nie 201.
Problem polega na tym, że jeśli chcę użyć Response
konstruktora do ustawienia kodu powrotu, muszę zwrócić Response
obiekt w mojej metodzie. Jak nadal zwracać User
obiekt do serializacji?
a) Ustaw kod w odpowiedzi serwletu
Jednym ze sposobów rozwiązania tego problemu jest uzyskanie obiektu żądania serwletu i samodzielne ustawienie kodu odpowiedzi, jak pokazano w odpowiedzi Garetta Wilsona:
@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;
}
Metoda nadal zwraca obiekt encji, a kod stanu będzie wynosił 201.
Zauważ, że aby zadziałało, musiałem opróżnić odpowiedź. Jest to nieprzyjemne odrodzenie niskiego poziomu kodu API serwletów w naszym ładnym zasobie JAX_RS, a co gorsza, powoduje to, że nagłówki są niemodyfikowalne po tym, ponieważ zostały już wysłane na drut.
b) Użyj obiektu odpowiedzi z bytem
W takim przypadku najlepszym rozwiązaniem jest użycie obiektu Response i ustawienie encji do serializacji na tym obiekcie odpowiedzi. Byłoby miło, gdyby obiekt Response był ogólny, aby wskazywał typ jednostki ładunku w tym przypadku, ale nie jest tak obecnie.
@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();
}
W takim przypadku używamy utworzonej metody klasy konstruktora odpowiedzi, aby ustawić kod statusu na 201. Przekazujemy obiekt encji (użytkownika) do odpowiedzi za pomocą metody encji ().
Rezultat jest taki, że kod HTTP to 401, tak jak chcieliśmy, a treść odpowiedzi jest dokładnie taka sama jak JSON, jak poprzednio, gdy zwracaliśmy obiekt User. Dodaje także nagłówek lokalizacji.
Klasa Response ma wiele metod konstruktora dla różnych statusów (stati?), Takich jak:
Response.accepted () Response.ok () Response.noContent () Response.notAcceptable ()
Uwaga: obiekt hateoas to klasa pomocnicza, którą opracowałem, aby pomóc w generowaniu identyfikatorów URI zasobów. Musisz wymyślić tutaj swój własny mechanizm;)
O to chodzi.
Mam nadzieję, że ta długa odpowiedź pomoże komuś :)