Comment fonctionne l'annotation Spring @ResponseBody?

89

J'ai une méthode qui est annotée de la manière suivante:

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

Donc je sais que par cette annotation:

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

cette méthode gère les requêtes HTTP GET adressées à la ressource représentée par l'URL / les commandes .

Cette méthode appelle un objet DAO qui renvoie un List .

Account représente un utilisateur sur le système et a des champs qui représentent cet utilisateur, quelque chose comme:

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

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

Ma question est la suivante: comment fonctionne exactement l' @ResponseBodyannotation?

Il est situé avant l' List<Account>objet retourné donc je pense qu'il fait référence à cette liste. La documentation du cours indique que cette annotation a pour fonction de:

assurez-vous que le résultat sera écrit dans la réponse HTTP par un convertisseur de messages HTTP (au lieu d'une vue MVC).

Et aussi lire sur la documentation officielle du printemps: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

il semble qu'il prend l' List<Account>objet et le met dans leHttp Response . Est-ce correct ou ai-je mal compris?

Dans le commentaire de la accountSummary()méthode précédente , il y a:

Vous devriez obtenir des résultats JSON lors de l'accès à http: // localhost: 8080 / rest-ws / app / accounts

Alors qu'est-ce que cela signifie exactement? Cela signifie-t-il que l' List<Account>objet retourné par la accountSummary()méthode est automatiquement converti au JSONformat puis mis dans leHttp Response ? Ou quoi?

Si cette assertion est vraie, où est-il spécifié que l'objet sera automatiquement converti au JSONformat? Le format standard est-il adopté lorsque l' @ResponseBodyannotation est utilisée ou est-il spécifié ailleurs?

AndreaNobili
la source

Réponses:

160

Tout d'abord, l'annotation n'annote pas List. Il annote la méthode, tout comme le RequestMappingfait. Votre code équivaut à

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

Maintenant, ce que signifie l'annotation, c'est que la valeur retournée de la méthode constituera le corps de la réponse HTTP. Bien entendu, une réponse HTTP ne peut pas contenir d'objets Java. Cette liste de comptes est donc transformée en un format adapté aux applications REST, généralement JSON ou XML.

Le choix du format dépend des convertisseurs de messages installés, des valeurs de l' producesattribut de l' @RequestMappingannotation et du type de contenu que le client accepte (qui est disponible dans les en-têtes de requête HTTP). Par exemple, si la demande indique qu'elle accepte XML, mais pas JSON, et qu'un convertisseur de message est installé qui peut transformer la liste en XML, alors XML sera renvoyé.

JB Nizet
la source
3
salut, comment configurer les "convertisseurs de messages installés"? Je veux que la valeur par défaut soit toujours convertie en Json. Suis-je capable de faire ça?
GMsoF
@JB Nizet pouvez-vous expliquer pourquoi la réponse http ne peut pas contenir d'objets java. Je suis un newbee pour java.
Naved Ali
3
@ Er.NavedAli lit la spécification http, le type de contenu de réponse http définit le contenu légal qu'une réponse http peut contenir. les valeurs légales pour cela peuvent être "application / octet-stream", "image / jpeg", "text / HTML", mais les objets java ne sont pas une valeur légale pour cela.
ZhaoGang
@ZhaoGang, le type de contenu 'application / json' a été remplacé par 'application / xml' dans mon cas de test, mais le corps de la réponse ne semble pas changer, toujours présent au format json.
LeafiWan
1
où exactement, je veux dire dans quelle classe de printemps, la décision de déterminer comment retourner la réponse est prise? Pouvez-vous m'indiquer la source sur github, où cette décision est prise ou le nom de la classe? Que se passera-t-il également si le client accepte XML, mais qu'aucun convertisseur XML n'est installé?
anir
63

La première chose de base à comprendre est la différence d'architectures.

À une extrémité, vous avez l'architecture MVC, qui est basée sur votre application Web normale, en utilisant des pages Web, et le navigateur demande une page:

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

Le navigateur fait une demande, le contrôleur (@Controller) récupère le modèle (@Entity) et crée la vue (JSP) à partir du modèle et la vue est renvoyée au client. Il s'agit de l'architecture de base de l'application Web.

De l'autre côté, vous avez une architecture RESTful. Dans ce cas, il n'y a pas de vue. Le Controller ne renvoie que le modèle (ou la représentation de ressource, en termes plus RESTful). Le client peut être une application JavaScript, une application serveur Java, toute application dans laquelle nous exposons notre API REST. Avec cette architecture, le client décide quoi faire avec ce modèle. Prenez par exemple Twitter. Twitter en tant qu'API Web (REST), qui permet à nos applications d'utiliser son API pour obtenir des informations telles que des mises à jour de statut, afin que nous puissions l'utiliser pour mettre ces données dans notre application. Ces données viendront dans un format comme JSON.

Cela étant dit, lorsque vous travaillez avec Spring MVC, il a d'abord été conçu pour gérer l'architecture de base des applications Web. Il existe différentes saveurs de signature de méthode qui permettent de produire une vue à partir de nos méthodes. La méthode peut renvoyer unModelAndView où nous la créons explicitement, ou il existe des moyens implicites où nous pouvons renvoyer un objet arbitraire qui est défini dans les attributs du modèle. Mais de toute façon, quelque part le long du cycle demande-réponse, une vue sera produite.

Mais lorsque nous utilisons @ResponseBody, nous disons que nous ne voulons pas qu'une vue soit produite. Nous voulons simplement envoyer l'objet de retour en tant que corps, dans le format que nous spécifions. Nous ne voudrions pas que ce soit un objet Java sérialisé (bien que possible). Donc oui, il doit être converti en un autre type commun (ce type est normalement traité par la négociation de contenu - voir le lien ci-dessous). Honnêtement, je ne travaille pas beaucoup avec Spring, même si j'essaye avec ça ici et là. Normalement, j'utilise

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

pour définir le type de contenu, mais peut-être que JSON est la valeur par défaut. Ne me citez pas, mais si vous obtenez JSON et que vous n'avez pas spécifié le produces, c'est peut-être la valeur par défaut. JSON n'est pas le seul format. Par exemple, ce qui précède pourrait facilement être envoyé en XML, mais vous auriez besoin d'avoir le producesto MediaType.APPLICATION_XML_VALUEet je pense que vous devez configurer le HttpMessageConverterpour JAXB. Quant au JSON MappingJacksonHttpMessageConverterconfiguré, lorsque nous avons Jackson sur le chemin de classe.

Je prendrais un certain temps pour en savoir plus sur la négociation de contenu . C'est une partie très importante de REST. Cela vous aidera à découvrir les différents formats de réponse et à les mapper à vos méthodes.

Paul Samsotha
la source
Si vous avez trouvé votre réponse lors de l'enquête sur la façon de créer un contrôleur REST générique / dynamique, sans utiliser @Controller/@RestController. J'ai découvert que je devais en quelque sorte omettre la couche de résolution de vue. Ce n'est pas si simple car la classe AbstractController fournit une méthode qui doit renvoyer le nom de la vue. J'ai posé une question à ce sujet: stackoverflow.com/questions/41016018/… , si vous avez des idées sur la façon de résoudre mon problème, veuillez poster un commentaire.
nowszy94
1

De plus, le type de retour est déterminé par

  1. Ce que la requête HTTP dit vouloir - dans son en-tête Accept. Essayez de regarder la demande initiale pour voir à quoi Accepter est défini.

  2. Ce que HttpMessageConverters Spring met en place. Spring MVC installera des convertisseurs pour XML (en utilisant JAXB) et JSON si les bibliothèques Jackson se trouvent sur le chemin de classe.

S'il y a un choix, il en choisit un - dans cet exemple, il se trouve qu'il s'agit de JSON.

Ceci est couvert dans les notes de cours. Recherchez les notes sur les convertisseurs de messages et la négociation de contenu.

paulchapman
la source