JAX-RS - Jak zwrócić kod stanu JSON i HTTP razem?

248

Piszę aplikację internetową REST (NetBeans 6.9, JAX-RS, TopLink Essentials) i próbuję zwrócić kod stanu JSON i HTTP. Mam gotowy i działający kod, który zwraca JSON po wywołaniu metody HTTP GET z klienta. Głównie:

@Path("get/id")
@GET
@Produces("application/json")
public M_機械 getMachineToUpdate(@PathParam("id") String id) {

    // some code to return JSON ...

    return myJson;
}

Ale chcę również zwrócić kod stanu HTTP (500, 200, 204 itd.) Wraz z danymi JSON.

Próbowałem użyć HttpServletResponse:

response.sendError("error message", 500);

Ale to sprawiło, że przeglądarka pomyślała, że ​​to „prawdziwa” 500, więc wyjściowa strona internetowa była zwykłą stroną błędu HTTP 500.

Chcę zwrócić kod stanu HTTP, aby mój JavaScript mógł obsługiwać logikę w zależności od niego (np. Wyświetlać kod błędu i komunikat na stronie HTML). Czy jest to możliwe, czy nie należy do tego używać kodów stanu HTTP?

Miauczeć
źródło
2
Jaka jest różnica między 500, które chcesz (nierealne? :)), a prawdziwą 500?
brzytwa
@razor Tutaj prawdziwa 500 oznacza stronę błędu HTML zamiast odpowiedzi JSON
Nupur
przeglądarka internetowa nie została zaprojektowana do pracy z JSON, ale ze stronami HTML, więc jeśli odpowiesz 500 (a nawet treścią wiadomości), przeglądarka może wyświetlać tylko komunikat o błędzie (naprawdę zależy od implementacji przeglądarki), tylko dlatego, że jest to przydatne normalny użytkownik.
brzytwa

Odpowiedzi:

346

Oto przykład:

@GET
@Path("retrieve/{uuid}")
public Response retrieveSomething(@PathParam("uuid") String uuid) {
    if(uuid == null || uuid.trim().length() == 0) {
        return Response.serverError().entity("UUID cannot be blank").build();
    }
    Entity entity = service.getById(uuid);
    if(entity == null) {
        return Response.status(Response.Status.NOT_FOUND).entity("Entity not found for UUID: " + uuid).build();
    }
    String json = //convert entity to json
    return Response.ok(json, MediaType.APPLICATION_JSON).build();
}

Spójrz na odpowiedź klasę .

Pamiętaj, że zawsze powinieneś określać typ zawartości, szczególnie jeśli przekazujesz wiele typów treści, ale jeśli każda wiadomość będzie reprezentowana jako JSON, możesz po prostu dodać adnotację do metody @Produces("application/json")

ospałość
źródło
12
Działa, ale nie podoba mi się wartość zwracana w odpowiedzi, ponieważ moim zdaniem zanieczyszcza twój kod, szczególnie w odniesieniu do każdego klienta, który próbuje go użyć. Jeśli udostępnisz interfejs zwracający odpowiedź osobie trzeciej, nie będzie wiedział, jakiego typu naprawdę zwracasz. Wiosna czyni to bardziej zrozumiałym dzięki adnotacji, bardzo przydatnej, jeśli zawsze zwracasz kod stanu (tj. HTTP 204)
Guido
19
Uczynienie tej klasy generyczną (Odpowiedź <T>) byłoby interesującym ulepszeniem Jax-rs, aby mieć zalety obu alternatyw.
Guido
41
Nie ma potrzeby jakoś konwertować encji na json. Możesz zrobić return Response.status(Response.Status.Forbidden).entity(myPOJO).build();Działa tak, jakbyś chciał return myPOJO;, ale z dodatkowym ustawieniem kodu statusu HTTP.
kratenko
1
Myślę, że podział logiki biznesowej na osobną klasę usług działa dobrze. Punkt końcowy używa odpowiedzi jako typu zwracanego, a jego metody to w większości tylko wywołania metod usług oraz adnotacje dotyczące ścieżki i parametrów. Czyści to logikę od mapowania typu adresu URL / treści (gdzie guma uderza w drogę, że tak powiem).
Stijn de Witt
w rzeczywistości można po prostu zwrócić obiekt nie zawijający się do odpowiedzi.
ses
191

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 OKprzypadku 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, ExceptionMapperktó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 ExceptionMapperklasy oraz dodać tych twórców map do Jersey za pomocą @Provideradnotacji ( ź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 Responsekonstruktora 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 Createdkodem 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ć Responsekonstruktora do ustawienia kodu powrotu, muszę zwrócić Responseobiekt w mojej metodzie. Jak nadal zwracać Userobiekt 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ś :)

Pierre Henry
źródło
Zastanawiam się, czy istnieje czysty sposób na zwrócenie samego obiektu danych zamiast odpowiedzi. To flushjest naprawdę brudne.
AlikElzin-kilaka
1
Tylko mój wkurzony zwierzak: 401 nie oznacza, że użytkownik nie jest autoryzowany. Oznacza to, że klient nie jest autoryzowany, ponieważ serwer nie wie, kim jesteś. Jeśli zalogowany / w inny sposób rozpoznany użytkownik nie może wykonać określonej czynności, poprawny kod odpowiedzi to 403 Zabronione.
Demonblack
69

Odpowiedź od jego zagłady zadziała, ale modyfikuje całe podejście do umożliwienia dostawcy, np. Jackson + JAXB, automatycznej konwersji zwróconego obiektu na jakiś format wyjściowy, taki jak JSON. Zainspirowany postem Apache CXF (który wykorzystuje klasę specyficzną dla CXF) znalazłem jeden sposób na ustawienie kodu odpowiedzi, który powinien działać w dowolnej implementacji JAX-RS: wstrzyknięcie kontekstu HttpServletResponse i ręczne ustawienie kodu odpowiedzi. Na przykład oto, jak ustawić kod odpowiedzi, CREATEDgdy jest to właściwe.

@Path("/foos/{fooId}")
@PUT
@Consumes("application/json")
@Produces("application/json")
public Foo setFoo(@PathParam("fooID") final String fooID, final Foo foo, @Context final HttpServletResponse response)
{
  //TODO store foo in persistent storage
  if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
  {
    response.setStatus(Response.Status.CREATED.getStatusCode());
  }
  return foo;  //TODO get latest foo from storage if needed
}

Ulepszenie: po znalezieniu innej pokrewnej odpowiedzi dowiedziałem się, że można wstrzyknąć HttpServletResponsezmienną składową, nawet dla klasy usługi singleton (przynajmniej w RESTEasy) !! Jest to znacznie lepsze podejście niż zanieczyszczenie interfejsu API szczegółami implementacji. Wyglądałoby to tak:

@Context  //injected response proxy supporting multiple threads
private HttpServletResponse response;

@Path("/foos/{fooId}")
@PUT
@Consumes("application/json")
@Produces("application/json")
public Foo setFoo(@PathParam("fooID") final String fooID, final Foo foo)
{
  //TODO store foo in persistent storage
  if(itemDidNotExistBefore) //return 201 only if new object; TODO app-specific logic
  {
    response.setStatus(Response.Status.CREATED.getStatusCode());
  }
  return foo;  //TODO get latest foo from storage if needed
}
Garret Wilson
źródło
W rzeczywistości można łączyć podejścia: opatrzyć komentarz tą metodą @Producesi nie określać typu nośnika w końcowej wersji Response.ok, a otrzymany obiekt zwrotny zostanie poprawnie zserializowany do JAXB w odpowiednim typie nośnika, aby pasował do żądania. (Właśnie próbowałem tego z jedną metodą, która mogłaby zwrócić albo XML, albo JSON: sama metoda też nie musi o tym wspominać, z wyjątkiem @Producesadnotacji.)
Royston Shufflebotham
Masz rację, Garret. Mój przykład był raczej ilustracją nacisku na dostarczenie typu treści. Nasze podejścia są podobne, ale pomysł użycia MessageBodyWritera Providerpozwala na niejawne negocjowanie treści, chociaż wydaje się, że w twoim przykładzie brakuje jakiegoś kodu. Oto kolejna odpowiedź, którą
podałem,
8
Nie mogę zastąpić kodu stanu w response.setStatus(). Jedynym sposobem wysłania na przykład odpowiedzi 404 „Nie znaleziono” jest ustawienie kodu statusu odpowiedzi response.setStatus(404)en, a następnie zamknięcie strumienia wyjściowego, response.getOutputStream().close()aby JAX-RS nie mógł zresetować mojego statusu.
Rob Juurlink
2
Byłem w stanie użyć tego podejścia do ustawienia kodu 201, ale musiałem dodać blok try-catch response.flushBuffer(), aby uniknąć przesłaniania kodu odpowiedzi przez ramę. Niezbyt czysty.
Pierre Henry
1
@RobJuurlink, jeśli chcesz konkretnie zwrócić a 404 Not Found, może być łatwiej po prostu użyć throw new NotFoundException().
Garret Wilson
34

Jeśli chcesz utrzymać warstwę zasobów w czystości od Responseobiektów, zalecamy użycie @NameBindingi powiązanie z implementacjami ContainerResponseFilter.

Oto treść adnotacji:

package my.webservice.annotations.status;

import javax.ws.rs.NameBinding;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Status {
  int CREATED = 201;
  int value();
}

Oto mięso filtra:

package my.webservice.interceptors.status;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;

@Provider
public class StatusFilter implements ContainerResponseFilter {

  @Override
  public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) throws IOException {
    if (containerResponseContext.getStatus() == 200) {
      for (Annotation annotation : containerResponseContext.getEntityAnnotations()) {
        if(annotation instanceof Status){
          containerResponseContext.setStatus(((Status) annotation).value());
          break;
        }
      }
    }
  }
}

A następnie implementacja zasobu staje się po prostu:

package my.webservice.resources;

import my.webservice.annotations.status.StatusCreated;
import javax.ws.rs.*;

@Path("/my-resource-path")
public class MyResource{
  @POST
  @Status(Status.CREATED)
  public boolean create(){
    return true;
  }
}
Nthalk
źródło
Utrzymuje interfejs API w czystości, ładna odpowiedź. Czy można sparametryzować adnotację jak @Status (kod = 205), a przechwytywacz zastąpi kod tym, co określisz? Myślę, że w zasadzie dałoby to adnotację na temat zastąpienia kodów, gdy tylko zajdzie taka potrzeba.
user2800708
@ user2800708, zrobiłem to już dla mojego kodu lokalnego, zaktualizowałem odpowiedź zgodnie z sugestią.
Nthalk
Fajnie dzięki. Dzięki tym i kilku innym sztuczkom w zasadzie mogę teraz wyczyścić interfejsy API REST w moim kodzie, dzięki czemu jest on zgodny z prostym interfejsem Java bez wzmianki o REST; to tylko kolejny mechanizm RMI.
user2800708
6
Zamiast zapętlania adnotacji w StatusFilter, możesz opatrzyć filtr adnotacjami @ Status oprócz @ Provider. Następnie filtr będzie wywoływany tylko w przypadku zasobów oznaczonych adnotacją @ Status. To jest cel @ NameBinding
trevorism
1
Dobry objaśnienie Trevorism. Jest jedna niezbyt miły efekt uboczny z adnotacji StatusFilterz @Status: albo trzeba dostarczyć domyślne adnotacji w valuepolu, albo zadeklarować jedną klasę podczas nanoszenia (np @Status(200)). To oczywiście nie jest idealne.
Phil,
6

Jeśli chcesz zmienić kod statusu z powodu wyjątku, w JAX-RS 2.0 możesz zaimplementować ExceptionMapper w ten sposób. To obsługuje ten rodzaj wyjątku dla całej aplikacji.

@Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<EJBAccessException> {

    @Override
    public Response toResponse(EJBAccessException exception) {
        return Response.status(Response.Status.UNAUTHORIZED.getStatusCode()).build();
    }

}
ercalamar
źródło
6

Jeśli Twój WS-RS wymaga zgłoszenia błędu, dlaczego nie skorzystać z wyjątku WebApplicationException?

@GET
@Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
@Path("{id}")
public MyEntity getFoo(@PathParam("id") long id,  @QueryParam("lang")long idLanguage) {

if (idLanguage== 0){
    // No URL parameter idLanguage was sent
    ResponseBuilder builder = Response.status(Response.Status.BAD_REQUEST);
    builder.entity("Missing idLanguage parameter on request");
    Response response = builder.build();
    throw new WebApplicationException(response);
    }
... //other stuff to return my entity
return myEntity;
}
Ariel
źródło
4
Moim zdaniem wyjątki WebApplicationException nie są odpowiednie dla błędów po stronie klienta, ponieważ generują ślady dużego stosu. Błędy klienta nie powinny wyrzucać śladów stosu po stronie serwera i zanieczyszczać go rejestrowaniem.
Rob Juurlink
5

JAX-RS obsługuje standardowe / niestandardowe kody HTTP. Zobacz ResponseBuilder i ResponseStatus, na przykład:

http://jackson.codehaus.org/javadoc/jax-rs/1.0/javax/ws/rs/core/Response.ResponseBuilder.html#status%28javax.ws.rs.core.Response.Status%29

Pamiętaj, że informacje JSON dotyczą bardziej danych związanych z zasobem / aplikacją. Kody HTTP odnoszą się bardziej do statusu żądanej operacji CRUD. (przynajmniej tak powinno być w systemach REST)

kvista
źródło
link jest zerwany
Umpa
5

Uważam, że bardzo przydatne jest zbudowanie również komunikatu JSON z powtarzającym się kodem:

@POST
@Consumes("application/json")
@Produces("application/json")
public Response authUser(JsonObject authData) {
    String email = authData.getString("email");
    String password = authData.getString("password");
    JSONObject json = new JSONObject();
    if (email.equalsIgnoreCase(user.getEmail()) && password.equalsIgnoreCase(user.getPassword())) {
        json.put("status", "success");
        json.put("code", Response.Status.OK.getStatusCode());
        json.put("message", "User " + authData.getString("email") + " authenticated.");
        return Response.ok(json.toString()).build();
    } else {
        json.put("status", "error");
        json.put("code", Response.Status.NOT_FOUND.getStatusCode());
        json.put("message", "User " + authData.getString("email") + " not found.");
        return Response.status(Response.Status.NOT_FOUND).entity(json.toString()).build();
    }
}
Szyk
źródło
4

Spójrz na przykład tutaj, najlepiej ilustrujący problem i sposób jego rozwiązania w najnowszej (2.3.1) wersji Jersey.

https://jersey.java.net/documentation/latest/representations.html#d0e3586

Zasadniczo polega na zdefiniowaniu niestandardowego wyjątku i zachowaniu typu zwracanego jako bytu. Gdy wystąpi błąd, zgłaszany jest wyjątek, w przeciwnym razie zwracane jest POJO.

ogłosić
źródło
Chciałbym dodać, że przykładem zainteresowania jest ten, w którym definiują własną klasę wyjątków i budują Responsew niej klasę wyjątków . Poszukaj CustomNotFoundExceptionklasy i może skopiuj ją do swojego postu.
JBert
Używam tego podejścia do błędów i podoba mi się. Nie ma to jednak zastosowania do kodów sukcesu (innych niż 200), takich jak „201 stworzony”.
Pierre Henry
3

Nie używam JAX-RS, ale mam podobny scenariusz, w którym używam:

response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
Czapki
źródło
Robi to za pomocą Spring MVC, ale jest łatwy sposób, aby się przekonać!
Caps,
1

Zauważ też, że domyślnie Jersey zastąpi treść odpowiedzi w przypadku kodu http 400 lub więcej.

Aby uzyskać określoną jednostkę jako treść odpowiedzi, spróbuj dodać następujący parametr inicjujący do swojego Jersey w pliku konfiguracyjnym web.xml:

    <init-param>
        <!-- used to overwrite default 4xx state pages -->
        <param-name>jersey.config.server.response.setStatusOverSendError</param-name>
        <param-value>true</param-value>
    </init-param>
Maxime T.
źródło
0

Poniższy kod działał dla mnie. Wstrzyknięcie messageContext za pomocą setera z adnotacjami i ustawienie kodu stanu w mojej metodzie „dodaj”.

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;

import org.apache.cxf.jaxrs.ext.MessageContext;

public class FlightReservationService {

    MessageContext messageContext;

    private final Map<Long, FlightReservation> flightReservations = new HashMap<>();

    @Context
    public void setMessageContext(MessageContext messageContext) {
        this.messageContext = messageContext;
    }

    @Override
    public Collection<FlightReservation> list() {
        return flightReservations.values();
    }

    @Path("/{id}")
    @Produces("application/json")
    @GET
    public FlightReservation get(Long id) {
        return flightReservations.get(id);
    }

    @Path("/")
    @Consumes("application/json")
    @Produces("application/json")
    @POST
    public void add(FlightReservation booking) {
        messageContext.getHttpServletResponse().setStatus(Response.Status.CREATED.getStatusCode());
        flightReservations.put(booking.getId(), booking);
    }

    @Path("/")
    @Consumes("application/json")
    @PUT
    public void update(FlightReservation booking) {
        flightReservations.remove(booking.getId());
        flightReservations.put(booking.getId(), booking);
    }

    @Path("/{id}")
    @DELETE
    public void remove(Long id) {
        flightReservations.remove(id);
    }
}
JBernhardt
źródło
0

Rozwijając na odpowiedź z Nthalk z Microprofile OpenAPI można wyrównać kod zwrotny z wykorzystaniem dokumentacji @APIResponse adnotacji.

Umożliwia to oznaczenie metody JAX-RS takiej jak

@GET
@APIResponse(responseCode = "204")
public Resource getResource(ResourceRequest request) 

Możesz parsować tę znormalizowaną adnotację za pomocą ContainerResponseFilter

@Provider
public class StatusFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
        if (responseContext.getStatus() == 200) {
            for (final var annotation : responseContext.getEntityAnnotations()) {
                if (annotation instanceof APIResponse response) {
                    final var rawCode = response.responseCode();
                    final var statusCode = Integer.parseInt(rawCode);

                    responseContext.setStatus(statusCode);
                }
            }
        }
    }

}

Zastrzeżenie występuje, gdy umieścisz wiele adnotacji w swojej metodzie jak

@APIResponse(responseCode = "201", description = "first use case")
@APIResponse(responseCode = "204", description = "because you can")
public Resource getResource(ResourceRequest request) 
to jest
źródło
-1

Używam jersey 2.0 z czytnikami treści i pisarzami. Miałem typ zwracanej metody jako konkretny byt, który był również używany przy implementacji programu piszącego treść komunikatu, i zwracałem to samo pojęcie, SkuListDTO. @GET @Consumes ({„application / xml”, „application / json”}) @Produces ({„application / xml”, „application / json”}) @Path („/ skuResync”)

public SkuResultListDTO getSkuData()
    ....
return SkuResultListDTO;

wszystko, co zmieniłem, to to, zostawiłem implementację programu piszącego i nadal działało.

public Response getSkuData()
...
return Response.status(Response.Status.FORBIDDEN).entity(dfCoreResultListDTO).build();
b. Phillips
źródło