Wie funktioniert die Spring @ ResponseBody-Annotation?

88

Ich habe eine Methode, die folgendermaßen kommentiert wird:

/**
* Provide a list of all accounts.
*/
//  TODO 02: Complete this method.  Add annotations to respond
//  to GET /accounts and return a List<Account> to be converted.
//  Save your work and restart the server.  You should get JSON results when accessing 
//  http://localhost:8080/rest-ws/app/accounts
@RequestMapping(value="/orders", method=RequestMethod.GET)
public @ResponseBody List<Account> accountSummary() {
    return accountManager.getAllAccounts();
}

Also ich weiß das durch diese Anmerkung:

@RequestMapping(value="/orders", method=RequestMethod.GET)

Diese Methode verarbeitet GET- HTTP-Anforderungen an die durch die URL / Bestellungen dargestellte Ressource .

Diese Methode ruft ein DAO-Objekt auf, das eine Liste zurückgibt .

Dabei stellt Konto einen Benutzer im System dar und verfügt über einige Felder, die diesen Benutzer darstellen, z. B.:

public class Account {

    @Id
    @Column(name = "ID")
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Long entityId;

    @Column(name = "NUMBER")
    private String number;

    @Column(name = "NAME")
    private String name;

    @OneToMany(cascade=CascadeType.ALL)
    @JoinColumn(name = "ACCOUNT_ID")
    private Set<Beneficiary> beneficiaries = new HashSet<Beneficiary>();

    ...............................
    ...............................
    ...............................
}

Meine Frage ist: Wie genau funktioniert die @ResponseBodyAnmerkung?

Es befindet sich vor dem zurückgegebenen List<Account>Objekt, daher denke ich, dass es sich auf diese Liste bezieht. In der Kursdokumentation heißt es, dass diese Anmerkung folgende Funktion erfüllt:

Stellen Sie sicher, dass das Ergebnis von einem HTTP-Nachrichtenkonverter (anstelle einer MVC-Ansicht) in die HTTP-Antwort geschrieben wird.

Lesen Sie auch die offizielle Spring-Dokumentation: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

es scheint, dass es das List<Account>Objekt nimmt und es in das legt Http Response. Ist das richtig oder verstehe ich falsch?

In den Kommentar der vorherigen accountSummary()Methode geschrieben steht:

Sie sollten JSON-Ergebnisse erhalten, wenn Sie auf http: // localhost: 8080 / rest-ws / app / accounts zugreifen

Was genau bedeutet das? Bedeutet dies, dass das List<Account>von der accountSummary()Methode zurückgegebene Objekt automatisch in das JSONFormat konvertiert und dann in das Format eingefügt wird Http Response? Oder was?

Wenn diese Behauptung wahr ist, wo wird angegeben, dass das Objekt automatisch in das JSONFormat konvertiert wird ? Wird das Standardformat übernommen, wenn die @ResponseBodyAnmerkung verwendet wird, oder wird es an anderer Stelle angegeben?

AndreaNobili
quelle

Antworten:

159

Erstens kommentiert die Annotation nicht List. Es kommentiert die Methode genauso wie RequestMappingsie. Ihr Code entspricht

@RequestMapping(value="/orders", method=RequestMethod.GET)
@ResponseBody
public List<Account> accountSummary() {
    return accountManager.getAllAccounts();
}

Die Annotation bedeutet nun, dass der zurückgegebene Wert der Methode den Hauptteil der HTTP-Antwort bildet. Natürlich kann eine HTTP-Antwort keine Java-Objekte enthalten. Daher wird diese Liste von Konten in ein Format umgewandelt, das für REST-Anwendungen geeignet ist, normalerweise JSON oder XML.

Die Auswahl des Formats hängt von den installierten Nachrichtenkonvertern, den Werten des producesAttributs der RequestMapping-Annotation und dem vom Client akzeptierten Inhaltstyp ab (der in den HTTP-Anforderungsheadern verfügbar ist). Wenn in der Anforderung beispielsweise angegeben wird, dass XML, jedoch nicht JSON akzeptiert wird und ein Nachrichtenkonverter installiert ist, der die Liste in XML umwandeln kann, wird XML zurückgegeben.

JB Nizet
quelle
3
Hallo, wie richten wir die "installierten Nachrichtenkonverter" ein? Ich möchte, dass die Standardeinstellung immer in Json konvertiert wird. Kann ich das machen?
GMsoF
@JB Nizet können Sie bitte erklären, warum http-Antwort keine Java-Objekte enthalten kann. Ich bin ein Neuling in Java.
Naved Ali
3
@ Er.NavedAli Lesen Sie die http-Spezifikation. Der Inhaltstyp der http-Antwort definiert den rechtlichen Inhalt, den eine http-Antwort enthalten kann. Zulässige Werte dafür können "application / octet-stream", "image / jpeg", "text / HTML" sein, aber Java-Objekte sind kein zulässiger Wert dafür.
ZhaoGang
@ZhaoGang, Der Inhaltstyp 'application / json' wurde in meinem Testfall durch 'application / xml' ersetzt, aber der Antworttext scheint sich nicht zu ändern, da das json-Format immer noch vorhanden ist.
LeafiWan
1
Wo genau, ich meine in welcher Frühjahrsklasse, wird die Entscheidung getroffen, wie die Antwort zurückgegeben werden soll? Können Sie mich auf Github verweisen, wo diese Entscheidung getroffen wird, oder auf den Klassennamen? Was passiert auch, wenn der Client XML akzeptiert, aber kein XML-Konverter installiert ist?
Anir
63

Das erste grundlegende, was zu verstehen ist, ist der Unterschied in den Architekturen.

An einem Ende haben Sie die MVC-Architektur, die auf Ihrer normalen Web-App basiert und Webseiten verwendet, und der Browser fordert eine Seite an:

Browser <---> Controller <---> Model
               |      |
               +-View-+

Der Browser stellt eine Anforderung, der Controller (@Controller) ruft das Modell (@Entity) ab und erstellt die Ansicht (JSP) aus dem Modell, und die Ansicht wird an den Client zurückgegeben. Dies ist die grundlegende Web-App-Architektur.

Am anderen Ende haben Sie eine RESTful-Architektur. In diesem Fall gibt es keine Ansicht. Der Controller sendet nur das Modell (oder die Ressourcendarstellung, genauer gesagt) zurück. Der Client kann eine JavaScript-Anwendung, eine Java-Serveranwendung oder eine beliebige Anwendung sein, für die wir unsere REST-API verfügbar machen. Bei dieser Architektur entscheidet der Client, was mit diesem Modell geschehen soll. Nehmen Sie zum Beispiel Twitter. Twitter als Web (REST) ​​-API, mit der unsere Anwendungen ihre API verwenden können, um beispielsweise Statusaktualisierungen abzurufen, damit wir diese Daten in unsere Anwendung einfügen können. Diese Daten werden in einem Format wie JSON vorliegen.

Bei der Arbeit mit Spring MVC wurde es jedoch zunächst für die grundlegende Webanwendungsarchitektur entwickelt. Es kann verschiedene Arten von Methodensignaturen geben, mit denen eine Ansicht aus unseren Methoden erstellt werden kann. Die Methode kann a zurückgeben, ModelAndViewwo wir es explizit erstellen, oder es gibt implizite Möglichkeiten, wie wir ein beliebiges Objekt zurückgeben können, das in Modellattribute gesetzt wird. In beiden Fällen wird jedoch irgendwo im Anforderungs- / Antwortzyklus eine Ansicht erstellt.

Aber wenn wir verwenden @ResponseBody, sagen wir, dass wir nicht wollen, dass eine Ansicht erzeugt wird. Wir möchten nur das Rückgabeobjekt als Body senden, in welchem ​​Format auch immer. Wir möchten nicht, dass es sich um ein serialisiertes Java-Objekt handelt (obwohl dies möglich ist). Ja, es muss in einen anderen gängigen Typ konvertiert werden (dieser Typ wird normalerweise durch Inhaltsverhandlung behandelt - siehe Link unten). Ehrlich gesagt arbeite ich nicht viel mit Spring, obwohl ich mich hier und da damit beschäftige. Normalerweise benutze ich

@RequestMapping(..., produces = MediaType.APPLICATION_JSON_VALUE)

um den Inhaltstyp festzulegen, aber möglicherweise ist JSON die Standardeinstellung. Zitieren Sie mich nicht, aber wenn Sie JSON erhalten und das nicht angegeben haben produces, ist es möglicherweise die Standardeinstellung. JSON ist nicht das einzige Format. Zum Beispiel kann die oben leicht in XML gesendet werden konnte , aber Sie würden die haben müssen , producesan MediaType.APPLICATION_XML_VALUEund ich glaube , Sie die konfigurieren müssen HttpMessageConverterfür JAXB. Wie für den JSON MappingJacksonHttpMessageConverterkonfiguriert, wenn wir Jackson auf dem Klassenpfad haben.

Ich würde einige Zeit brauchen, um etwas über Content Negotiation zu lernen . Es ist ein sehr wichtiger Teil von REST. Hier erfahren Sie mehr über die verschiedenen Antwortformate und wie Sie sie Ihren Methoden zuordnen können.

Paul Samsotha
quelle
Wenn Sie Ihre Antwort gefunden haben, während Sie untersucht haben, wie Sie einen generischen / dynamischen REST-Controller ohne Verwendung erstellen können @Controller/@RestController. Ich habe festgestellt, dass ich die View Resolver-Ebene irgendwie weglassen muss. Dies ist nicht so einfach, da die AbstractController-Klasse eine Methode bereitstellt, die den Ansichtsnamen zurückgeben muss. Ich habe eine Frage dazu gestellt: stackoverflow.com/questions/41016018/… . Wenn Sie Ideen haben, wie ich mein Problem lösen kann , schreiben Sie bitte einen Kommentar.
nowszy94
1

Darüber hinaus wird der Rückgabetyp durch bestimmt

  1. Was die HTTP-Anfrage sagt - in ihrem Accept-Header. Sehen Sie sich die erste Anforderung an, um festzustellen, auf was Akzeptieren eingestellt ist.

  2. Was HttpMessageConverters Spring einrichtet. Spring MVC richtet Konverter für XML (unter Verwendung von JAXB) und JSON ein, wenn sich Jackson-Bibliotheken im Klassenpfad befinden.

Wenn es eine Auswahl gibt, wird eine ausgewählt - in diesem Beispiel handelt es sich zufällig um JSON.

Dies wird in den Kursnotizen behandelt. Beachten Sie die Hinweise zu Message Converters und Content Negotiation.

Paulchapman
quelle