JAX-RS - Wie kann man JSON- und HTTP-Statuscode zusammen zurückgeben?

248

Ich schreibe eine REST-Web-App (NetBeans 6.9, JAX-RS, TopLink Essentials) und versuche, JSON- und HTTP-Statuscode zurückzugeben. Ich habe Code bereit und funktioniert, der JSON zurückgibt, wenn die HTTP-GET-Methode vom Client aufgerufen wird. Im Wesentlichen:

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

    // some code to return JSON ...

    return myJson;
}

Ich möchte aber auch einen HTTP-Statuscode (500, 200, 204 usw.) zusammen mit den JSON-Daten zurückgeben.

Ich habe versucht zu verwenden HttpServletResponse:

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

Dies ließ den Browser jedoch denken, dass es sich um eine "echte" 500 handelt, sodass die Ausgabe-Webseite eine reguläre HTTP 500-Fehlerseite war.

Ich möchte einen HTTP-Statuscode zurückgeben, damit mein clientseitiges JavaScript abhängig davon eine gewisse Logik verarbeiten kann (z. B. um den Fehlercode und die Meldung auf einer HTML-Seite anzuzeigen). Ist dies möglich oder sollten für solche Dinge keine HTTP-Statuscodes verwendet werden?

Miau
quelle
2
Was ist der Unterschied zwischen 500, die Sie wollen (unwirklich? :)) und echten 500?
Rasiermesser
@razor Hier bedeutet real 500 eine HTML-Fehlerseite anstelle einer JSON-Antwort
Nupur
Der Webbrowser wurde nicht für die Verwendung mit JSON entwickelt, sondern mit HTML-Seiten. Wenn Sie also mit 500 (und sogar einem Nachrichtentext) antworten, kann der Browser nur eine Fehlermeldung anzeigen (hängt wirklich von der Browserimplementierung ab), nur weil dies nützlich ist ein normaler Benutzer.
Rasiermesser

Antworten:

347

Hier ist ein Beispiel:

@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();
}

Schauen Sie sich die Response- Klasse an.

Beachten Sie, dass Sie immer einen Inhaltstyp angeben sollten, insbesondere wenn Sie mehrere Inhaltstypen übergeben. Wenn jedoch jede Nachricht als JSON dargestellt wird, können Sie die Methode einfach mit Anmerkungen versehen @Produces("application/json")

Hisdrewness
quelle
12
Es funktioniert, aber was mir am Rückgabewert "Antwort" nicht gefällt, ist, dass es meiner Meinung nach Ihren Code verschmutzt, insbesondere in Bezug auf jeden Client, der versucht, ihn zu verwenden. Wenn Sie eine Schnittstelle bereitstellen, die eine Antwort an einen Dritten zurückgibt, weiß dieser nicht, welchen Typ Sie wirklich zurückgeben. Spring macht es mit einer Anmerkung klarer, sehr nützlich, wenn Sie immer einen Statuscode (dh HTTP 204) zurückgeben
Guido
19
Diese Klasse generisch zu machen (Response <T>) wäre eine interessante Verbesserung für jax-rs, um die Vorteile beider Alternativen zu nutzen.
Guido
41
Es ist nicht nötig, die Entität irgendwie in json zu konvertieren. Sie können so arbeiten, return Response.status(Response.Status.Forbidden).entity(myPOJO).build();als ob Sie es tun würden return myPOJO;, jedoch mit zusätzlicher Einstellung des HTTP-Statuscodes.
Kratenko
1
Ich denke, die Aufteilung der Geschäftslogik in eine separate Serviceklasse funktioniert gut. Der Endpunkt verwendet Response als Rückgabetyp. Bei den Methoden handelt es sich meist nur um Aufrufe der Servicemethoden sowie der Pfad- und Parameteranmerkungen. Es trennt die Logik sauber von der Zuordnung von URL / Inhaltstyp (wo der Gummi sozusagen auf die Straße trifft).
Stijn de Witt
Tatsächlich kann man das Objekt, das nicht in die Antwort eingeschlossen ist, einfach zurückgeben.
ses
191

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 OKbeim 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 ExceptionMappermit 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 ExceptionMapperKlassen definieren und diese Zuordnungen mithilfe der @ProviderAnmerkung ( 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 ResponseBuilder 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 ( POSTAnforderung) erstellen und Informationen zu dieser neuen Entität oder möglicherweise zur Entität selbst zusammen mit einem 201 CreatedStatuscode 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 Responseein ResponseObjekt in meiner Methode zurückgeben muss, wenn ich den Builder zum Festlegen des Rückkehrcodes verwenden möchte . Wie kann ich das Userzu 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 :)

Pierre Henry
quelle
Ich frage mich, ob es eine saubere Möglichkeit gibt, das Datenobjekt selbst anstelle der Antwort zurückzugeben. Das flushist ja dreckig.
AlikElzin-Kilaka
1
Nur ein kleiner Ärger von mir: 401 bedeutet nicht, dass der Benutzer nicht autorisiert ist. Dies bedeutet, dass der Client nicht autorisiert ist, da der Server nicht weiß, wer Sie sind. Wenn ein protokollierter / anderweitig erkannter Benutzer eine bestimmte Aktion nicht ausführen darf, lautet der richtige Antwortcode 403 Verboten.
Demonblack
69

Die Antwort von hisdrewness wird funktionieren, ändert jedoch den gesamten Ansatz dahingehend, dass ein Anbieter wie Jackson + JAXB Ihr zurückgegebenes Objekt automatisch in ein Ausgabeformat wie JSON konvertiert. Inspiriert von einem Apache CXF- Beitrag (der eine CXF-spezifische Klasse verwendet) habe ich eine Möglichkeit gefunden, den Antwortcode festzulegen, der in jeder JAX-RS-Implementierung funktionieren soll: Einfügen eines HttpServletResponse-Kontexts und manuelles Festlegen des Antwortcodes. Hier erfahren Sie beispielsweise, wie Sie den Antwortcode CREATEDgegebenenfalls einstellen .

@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
}

Verbesserung: Nachdem ich eine andere verwandte Antwort gefunden hatte , erfuhr ich, dass man die HttpServletResponseVariable als Mitglied auch für Singleton-Serviceklassen (zumindest in RESTEasy) einfügen kann !! Dies ist ein viel besserer Ansatz, als die API mit Implementierungsdetails zu verschmutzen. Es würde so aussehen:

@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
quelle
Sie können die Ansätze tatsächlich kombinieren: Kommentieren Sie die Methode mit @Producesund geben Sie den Medientyp im Finale nicht an Response.ok, und Sie erhalten Ihr Rückgabeobjekt korrekt JAXB-serialisiert in den entsprechenden Medientyp, um der Anforderung zu entsprechen. (Ich habe dies nur mit einer einzigen Methode versucht, die entweder XML oder JSON zurückgeben kann: Die Methode selbst muss auch nicht erwähnt werden, außer in der @ProducesAnmerkung.)
Royston Shufflebotham
Du hast recht, Garret. Mein Beispiel war eher ein Beispiel für die Betonung der Bereitstellung eines Inhaltstyps. Unsere Ansätze sind ähnlich, aber die Idee, aMessageBodyWriter und zu verwenden, Providerermöglicht implizite Inhaltsverhandlungen, obwohl Ihrem Beispiel anscheinend Code fehlt. Hier ist eine andere Antwort, die ich gegeben habe, die dies veranschaulicht: stackoverflow.com/questions/5161466/…
hisdrewness
8
Ich kann den Statuscode in nicht überschreiben response.setStatus(). Die einzige Möglichkeit, beispielsweise eine 404 Not Found- Antwort zu senden, besteht darin, den Antwortstatuscode festzulegen response.setStatus(404)und dann den Ausgabestream zu schließenresponse.getOutputStream().close() damit JAX-RS meinen Status nicht zurücksetzen kann.
Rob Juurlink
2
Ich konnte diesen Ansatz verwenden, um einen 201-Code festzulegen, musste jedoch einen Try-Catch-Block hinzufügen, response.flushBuffer()um zu vermeiden, dass das Framework meinen Antwortcode überschreibt. Nicht sehr sauber.
Pierre Henry
1
@RobJuurlink, wenn Sie speziell a zurückgeben möchten 404 Not Found, ist es möglicherweise einfacher, nur zu verwenden throw new NotFoundException().
Garret Wilson
34

Wenn Sie Ihre Ressourcenschicht frei von ResponseObjekten halten möchten , empfehle ich die Verwendung @NameBindingund Bindung an Implementierungen vonContainerResponseFilter .

Hier ist das Fleisch der Anmerkung:

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();
}

Hier ist das Fleisch des Filters:

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;
        }
      }
    }
  }
}

Und dann wird die Implementierung auf Ihrer Ressource einfach:

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
quelle
Hält die API sauber, nette Antwort. Wäre es möglich, Ihre Annotation wie @Status (Code = 205) zu parametrisieren und den Interceptor den Code durch den von Ihnen angegebenen ersetzen zu lassen? Ich denke, das würde Ihnen im Grunde eine Anmerkung zum Überschreiben der Codes geben, wann immer Sie müssen.
user2800708
@ user2800708, ich habe dies bereits für meinen lokalen Code getan und die Antwort wie von Ihnen vorgeschlagen aktualisiert.
Nthalk
Nett, danke. Mit diesem und einigen anderen Tricks kann ich jetzt die REST-APIs in meinem Code bereinigen, sodass sie einer einfachen Java-Schnittstelle entsprechen, in der REST nicht erwähnt wird. Es ist nur ein weiterer RMI-Mechanismus.
user2800708
6
Anstatt in StatusFilter nach Anmerkungen zu suchen, können Sie den Filter zusätzlich zu @ Provider mit @ Status versehen. Dann wird der Filter nur für Ressourcen aufgerufen, die mit @ Status versehen sind. Dies ist der Zweck von @ NameBinding
trevorism
1
Gute Bezeichnung @trevorism. Es gibt einen nicht so schönen Nebeneffekt beim Annotieren StatusFiltermit @Status: Sie müssen entweder einen Standard für das valueFeld der Annotation angeben oder einen deklarieren, wenn Sie die Klasse annotieren ( z. B. :) @Status(200). Dies ist offensichtlich nicht ideal.
Phil
6

Wenn Sie den Statuscode aufgrund einer Ausnahme ändern möchten, können Sie mit JAX-RS 2.0 einen solchen ExceptionMapper implementieren. Dies behandelt diese Art von Ausnahme für die gesamte App.

@Provider
public class UnauthorizedExceptionMapper implements ExceptionMapper<EJBAccessException> {

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

}
ercalamar
quelle
6

Wenn Ihr WS-RS einen Fehler auslösen muss, verwenden Sie einfach die 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
quelle
4
Meiner Meinung nach sind WebApplicationExceptions nicht für clientseitige Fehler geeignet, da sie große Stapelspuren auslösen. Clientfehler sollten keine serverseitigen Stack-Traces auslösen und die Protokollierung damit verschmutzen.
Rob Juurlink
5

JAX-RS unterstützt Standard- / benutzerdefinierte HTTP-Codes. Siehe ResponseBuilder und ResponseStatus, zum Beispiel:

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

Beachten Sie, dass sich JSON-Informationen eher auf die mit der Ressource / Anwendung verknüpften Daten beziehen. In den HTTP-Codes wird mehr über den Status der angeforderten CRUD-Operation berichtet. (Zumindest soll es so in REST-fähigen Systemen sein)

kvista
quelle
Die Verbindung ist unterbrochen
Umpa
5

Ich fand es sehr nützlich, auch eine JSON-Nachricht mit wiederholtem Code zu erstellen, wie folgt:

@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();
    }
}
Array
quelle
4

Schauen Sie sich das Beispiel hier an, es veranschaulicht das Problem am besten und wie es in der neuesten (2.3.1) Version von Jersey gelöst wird.

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

Grundsätzlich müssen Sie eine benutzerdefinierte Ausnahme definieren und den Rückgabetyp als Entität beibehalten. Wenn ein Fehler auftritt, wird die Ausnahme ausgelöst, andernfalls geben Sie das POJO zurück.

annouk
quelle
Ich möchte hinzufügen, dass das Beispiel von Interesse das ist, bei dem sie ihre eigene Ausnahmeklasse definieren und eine Responsedarin erstellen . CustomNotFoundExceptionSuchen Sie einfach nach der Klasse und kopieren Sie sie möglicherweise in Ihren Beitrag.
JBert
Ich benutze diesen Ansatz für Fehler und ich mag es. Dies gilt jedoch nicht für Erfolgscodes (anders als 200) wie "201 erstellt".
Pierre Henry
3

Ich verwende JAX-RS nicht, aber ich habe ein ähnliches Szenario, in dem ich Folgendes verwende:

response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
Kappen
quelle
Es funktioniert für mich mit Spring MVC, aber es gibt einen einfachen Weg, es herauszufinden!
Caps
1

Beachten Sie außerdem, dass Jersey bei einem HTTP-Code 400 oder höher standardmäßig den Antworttext überschreibt.

Versuchen Sie, Ihrem Jersey in Ihrer Konfigurationsdatei web.xml den folgenden init-Parameter hinzuzufügen, um Ihre angegebene Entität als Antworttext zu erhalten:

    <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.
quelle
0

Der folgende Code hat bei mir funktioniert. Injizieren des messageContext über einen mit Anmerkungen versehenen Setter und Festlegen des Statuscodes in meiner "add" -Methode.

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
quelle
0

Wenn Sie die Antwort von Nthalk mit Microprofile OpenAPI erweitern , können Sie den Rückkehrcode mithilfe von @APIResponse an Ihrer Dokumentation ausrichten Annotation .

Dies ermöglicht das Markieren einer JAX-RS-Methode wie

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

Sie können diese standardisierte Annotation mit einem ContainerResponseFilter analysieren

@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);
                }
            }
        }
    }

}

Eine Einschränkung tritt auf, wenn Sie Ihrer Methode wie mehrere Anmerkungen hinzufügen

@APIResponse(responseCode = "201", description = "first use case")
@APIResponse(responseCode = "204", description = "because you can")
public Resource getResource(ResourceRequest request) 
thatsIch
quelle
-1

Ich verwende Jersey 2.0 mit Lesern und Schreibern von Nachrichtenkörpern. Ich hatte meinen Methodenrückgabetyp als eine bestimmte Entität, die auch bei der Implementierung des Nachrichtentextschreibers verwendet wurde, und ich gab das gleiche Pojo zurück, ein SkuListDTO. @GET @Consumes ({"application / xml", "application / json"}) @Produces ({"application / xml", "application / json"}) @Path ("/ skuResync")

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

Alles, was ich geändert habe, war Folgendes: Ich habe die Writer-Implementierung in Ruhe gelassen und es hat immer noch funktioniert.

public Response getSkuData()
...
return Response.status(Response.Status.FORBIDDEN).entity(dfCoreResultListDTO).build();
b. Phillips
quelle