Come funziona l'annotazione Spring @ResponseBody?


89

Ho un metodo annotato nel modo seguente:

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

Quindi so che da questa annotazione:

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

questo metodo gestisce le richieste GET HTTP fatte alla risorsa rappresentata dall'URL / orders .

Questo metodo chiama un oggetto DAO che restituisce un List .

dove Account rappresenta un utente nel sistema e ha alcuni campi che rappresentano questo utente, qualcosa come:

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

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

La mia domanda è: come funziona esattamente l' @ResponseBodyannotazione?

Si trova prima List<Account>dell'oggetto restituito , quindi penso che si riferisca a questo elenco. La documentazione del corso afferma che questa annotazione ha la funzione di:

assicurarsi che il risultato venga scritto nella risposta HTTP da un convertitore di messaggi HTTP (invece di una vista MVC).

E leggendo anche sulla documentazione ufficiale di primavera: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

sembra che prenda l' List<Account>oggetto e lo metta nel file Http Response. È corretto o sto fraintendendo?

Scritto nel commento del accountSummary()metodo precedente c'è:

Dovresti ottenere risultati JSON quando accedi a http: // localhost: 8080 / rest-ws / app / accounts

Quindi cosa significa esattamente? Significa che l' List<Account>oggetto restituito dal accountSummary()metodo viene automaticamente convertito in JSONformato e quindi inserito nel file Http Response? O cosa?

Se questa affermazione è vera, dove viene specificato che l'oggetto verrà automaticamente convertito in JSONformato? Il formato standard viene adottato quando @ResponseBodysi utilizza l' annotazione o è specificato altrove?

Risposte:


160

Prima di tutto, l'annotazione non annota List. Annota il metodo, proprio come RequestMappingfa. Il tuo codice è equivalente a

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

Ciò che significa l'annotazione è che il valore restituito dal metodo costituirà il corpo della risposta HTTP. Ovviamente, una risposta HTTP non può contenere oggetti Java. Quindi questo elenco di account viene trasformato in un formato adatto per le applicazioni REST, in genere JSON o XML.

La scelta del formato dipende dai convertitori messaggi installati, sui valori della producesdell'attributo del @RequestMappingannotazioni, e il tipo di contenuto che il cliente accetta (che è disponibile nelle intestazioni di richiesta HTTP). Ad esempio, se la richiesta dice che accetta XML, ma non JSON, ed è installato un convertitore di messaggi che può trasformare l'elenco in XML, verrà restituito XML.


3
ciao, come configuriamo i "convertitori di messaggi installati"? Voglio che il valore predefinito venga sempre convertito in Json. Sono in grado di farlo?
GMsoF

@JB Nizet puoi spiegare perché la risposta http non può contenere oggetti java. Sono un neofita di Java.
Naved Ali

3
@ Er.NavedAli legge la specifica http, il tipo di contenuto della risposta http definisce il contenuto legale che può contenere una risposta http. i valori legali per questo possono essere "application / octet-stream", "image / jpeg", "text / HTML", ma gli oggetti java non sono un valore legale per esso.
ZhaoGang

@ZhaoGang, il tipo di contenuto "application / json" è stato sostituito da "application / xml" nel mio caso di test, ma il corpo della risposta non sembra essere cambiato, è ancora presente il formato json.
LeafiWan

1
dove esattamente, intendo in quale classe primaverile, viene presa la decisione di determinare come restituire la risposta? Puoi indicarmi la fonte su GitHub, dove viene presa questa decisione o il nome della classe? Inoltre cosa succederà se il client accetta XML, ma non è installato alcun convertitore XML?
anir

63

La prima cosa fondamentale da capire è la differenza nelle architetture.

Un'estremità ha l'architettura MVC, che si basa sulla normale app Web, che utilizza pagine Web e il browser effettua una richiesta per una pagina:

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

Il browser effettua una richiesta, il controller (@Controller) ottiene il modello (@Entity) e crea la vista (JSP) dal modello e la vista viene restituita al client. Questa è l'architettura di base dell'app Web.

Dall'altra parte, hai un'architettura RESTful. In questo caso, non c'è Vista. Il Controller restituisce solo il modello (o la rappresentazione della risorsa, in termini più RESTful). Il client può essere un'applicazione JavaScript, un'applicazione server Java, qualsiasi applicazione a cui esponiamo la nostra API REST. Con questa architettura, il cliente decide cosa fare con questo modello. Prendi ad esempio Twitter. Twitter come API Web (REST), che consente alle nostre applicazioni di utilizzare la sua API per ottenere cose come gli aggiornamenti di stato, in modo che possiamo usarla per inserire quei dati nella nostra applicazione. Quei dati arriveranno in un formato come JSON.

Detto questo, quando si lavora con Spring MVC, è stato inizialmente creato per gestire l'architettura di base dell'applicazione Web. Ci possono essere diversi gusti di firma del metodo che consentono di produrre una vista dai nostri metodi. Il metodo potrebbe restituire a ModelAndViewdove lo creiamo esplicitamente, oppure ci sono modi impliciti in cui possiamo restituire un oggetto arbitrario che viene impostato negli attributi del modello. Ma in ogni caso, da qualche parte lungo il ciclo richiesta-risposta, verrà prodotta una vista.

Ma quando usiamo @ResponseBody, stiamo dicendo che non vogliamo che venga prodotta una vista. Vogliamo solo inviare l'oggetto di ritorno come corpo, in qualsiasi formato specifichiamo. Non vorremmo che fosse un oggetto Java serializzato (sebbene possibile). Quindi sì, deve essere convertito in un altro tipo comune (questo tipo viene normalmente gestito attraverso la negoziazione del contenuto - vedere il collegamento sotto). Onestamente, non lavoro molto con la primavera, anche se mi diletto qua e là. Normalmente, io uso

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

per impostare il tipo di contenuto, ma forse JSON è l'impostazione predefinita. Non citarmi, ma se stai ottenendo JSON e non hai specificato produces, allora forse è l'impostazione predefinita. JSON non è l'unico formato. Ad esempio, quanto sopra potrebbe essere facilmente inviato in XML, ma dovresti avere il producesto MediaType.APPLICATION_XML_VALUEe credo che tu debba configurare il HttpMessageConverterper JAXB. Per quanto riguarda il JSON MappingJacksonHttpMessageConverterconfigurato, quando abbiamo Jackson sul classpath.

Vorrei dedicare del tempo per conoscere la negoziazione dei contenuti . È una parte molto importante di REST. Ti aiuterà a conoscere i diversi formati di risposta e come associarli ai tuoi metodi.


Se hai trovato la tua risposta durante le indagini su come creare un controller REST generico / dinamico, senza utilizzare @Controller/@RestController. Ho scoperto che ho bisogno di omettere in qualche modo il livello di risoluzione della vista. Non è così semplice perché la classe AbstractController fornisce un metodo che deve restituire il nome della vista. Ho posto una domanda al riguardo: stackoverflow.com/questions/41016018/… , se hai qualche idea su come posso risolvere il mio problema, per favore pubblica un commento.
nowszy94

1

Inoltre, il tipo di ritorno è determinato da

  1. Ciò che la richiesta HTTP dice di volere - nella sua intestazione Accept. Prova a guardare la richiesta iniziale per vedere su cosa è impostato Accetta.

  2. Cosa imposta HttpMessageConverters Spring. Spring MVC configurerà i convertitori per XML (usando JAXB) e JSON se le librerie Jackson si trovano nel percorso di classe.

Se c'è una scelta, ne sceglie una: in questo esempio, è JSON.

Questo è trattato nelle note del corso. Cerca le note sui convertitori di messaggi e sulla negoziazione dei contenuti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.