Jak działa adnotacja Spring @ResponseBody?

89

Mam metodę, która jest opisana w następujący sposób:

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

Więc wiem, że dzięki tej adnotacji:

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

ta metoda obsługuje żądania GET HTTP skierowane do zasobu reprezentowanego przez adres URL / zamówienia .

Ta metoda wywołuje obiekt DAO, który zwraca List .

gdzie Konto reprezentuje użytkownika w systemie i ma kilka pól, które reprezentują tego użytkownika, na przykład:

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

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

Moje pytanie brzmi: jak dokładnie działa @ResponseBodyadnotacja?

Znajduje się przed zwróconym List<Account>obiektem, więc myślę, że odwołuje się do tej listy. Dokumentacja kursu stwierdza, że ​​ta adnotacja służy do:

upewnij się, że wynik zostanie zapisany w odpowiedzi HTTP przez konwerter komunikatów HTTP (zamiast widoku MVC).

A także czytanie oficjalnej dokumentacji Spring: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

wydaje się, że bierze List<Account>przedmiot i wkłada go do Http Response. Czy to prawda, czy nie rozumiem?

W komentarzu do poprzedniej accountSummary()metody wpisano:

Powinieneś otrzymać wyniki JSON podczas uzyskiwania dostępu do http: // localhost: 8080 / rest-ws / app / accounts

Więc co to dokładnie oznacza? Czy to oznacza, że List<Account>obiekt zwracany przez accountSummary()metodę jest automatycznie konwertowany do JSONformatu, a następnie umieszczany w Http Response? Albo co?

Jeśli to stwierdzenie jest prawdziwe, gdzie określono, że obiekt zostanie automatycznie przekonwertowany na JSONformat? Czy przyjęto standardowy format, gdy @ResponseBodyjest używana adnotacja, czy też określono go gdzie indziej?

AndreaNobili
źródło

Odpowiedzi:

160

Przede wszystkim adnotacja nie zawiera adnotacji List. Opisuje metodę, tak samo jak RequestMapping. Twój kod jest równoważny z

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

Teraz adnotacja oznacza, że ​​zwrócona wartość metody będzie stanowić treść odpowiedzi HTTP. Oczywiście odpowiedź HTTP nie może zawierać obiektów Java. Dlatego ta lista kont jest przekształcana do formatu odpowiedniego dla aplikacji REST, zazwyczaj JSON lub XML.

Wybór formatu zależy od zainstalowanych konwerterów wiadomości, o wartości producesatrybutu z @RequestMappingadnotacją, oraz o treści typu, które klient akceptuje (który jest dostępny w nagłówkach żądania HTTP). Na przykład, jeśli żądanie mówi, że akceptuje XML, ale nie JSON, i jest zainstalowany konwerter komunikatów, który może przekształcić listę na XML, zostanie zwrócony kod XML.

JB Nizet
źródło
3
cześć, jak ustawić „zainstalowane konwertery wiadomości”? Chcę, aby wartość domyślna zawsze była konwertowana na Json. Czy jestem w stanie to zrobić?
Sam YC
@JB Nizet Czy możesz wyjaśnić, dlaczego odpowiedź http nie może zawierać obiektów java. Jestem nowicjuszem w Javie.
Naved Ali
3
@ Er.NavedAli odczytuje specyfikację http, typ zawartości odpowiedzi http definiuje legalną treść, jaką może zawierać odpowiedź http. dopuszczalnymi wartościami mogą być „application / octet-stream”, „image / jpeg”, „text / HTML”, ale obiekty java nie są dla nich prawidłową wartością.
ZhaoGang
@ZhaoGang, Content-type „application / json” został zastąpiony przez „application / xml” w moim przypadku testowym, ale treść odpowiedzi wydaje się być bez zmian, nadal obecny format json.
LeafiWan
1
gdzie dokładnie, mam na myśli w której klasie wiosennej zapada decyzja o tym, jak zwrócić odpowiedź? Czy możesz wskazać mi źródło na githubie, gdzie podjęto taką decyzję lub nazwę klasy? Co się stanie, jeśli klient zaakceptuje XML, ale nie jest zainstalowany żaden konwerter XML?
anir
65

Pierwszą podstawową rzeczą do zrozumienia jest różnica w architekturach.

Z jednej strony masz architekturę MVC, która jest oparta na normalnej aplikacji internetowej, korzystającej ze stron internetowych, a przeglądarka wysyła żądanie strony:

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

Przeglądarka wysyła żądanie, kontroler (@Controller) pobiera model (@Entity) i tworzy widok (JSP) z modelu, a widok jest zwracany klientowi. To jest podstawowa architektura aplikacji internetowych.

Z drugiej strony masz architekturę RESTful. W tym przypadku nie ma widoku. Kontroler odsyła tylko model (lub reprezentację zasobów, w bardziej RESTful sposób). Klientem może być aplikacja JavaScript, aplikacja serwerowa Java, dowolna aplikacja, w której udostępniamy nasz REST API. W przypadku tej architektury klient decyduje, co zrobić z tym modelem. Weźmy na przykład Twitter. Twitter jako interfejs API sieci Web (REST), który umożliwia naszym aplikacjom korzystanie z jego interfejsu API w celu pobierania takich rzeczy, jak aktualizacje statusu, dzięki czemu możemy go używać do umieszczania tych danych w naszej aplikacji. Te dane będą miały format, na przykład JSON.

To powiedziawszy, podczas pracy ze Spring MVC został on najpierw zbudowany do obsługi podstawowej architektury aplikacji internetowych. Istnieje wiele różnych charakterystyk metod, które pozwalają na stworzenie widoku z naszych metod. Metoda może zwrócić a ModelAndViewtam, gdzie jawnie ją utworzymy, lub istnieją niejawne sposoby, w których możemy zwrócić dowolny obiekt, który zostanie ustawiony na atrybuty modelu. Ale tak czy inaczej, gdzieś w trakcie cyklu żądanie-odpowiedź zostanie utworzony widok.

Ale kiedy używamy @ResponseBody, mówimy, że nie chcemy, aby powstał widok. Chcemy po prostu wysłać obiekt zwracany jako ciało, w jakimkolwiek określonym przez nas formacie. Nie chcielibyśmy, aby był to zserializowany obiekt Java (choć jest to możliwe). Więc tak, musi zostać przekonwertowany na inny popularny typ (ten typ jest zwykle obsługiwany poprzez negocjację treści - patrz link poniżej). Szczerze mówiąc, nie pracuję dużo ze Springem, chociaż bawię się tym tu i tam. Zwykle używam

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

aby ustawić typ zawartości, ale być może JSON jest domyślnym. Nie cytuj mnie, ale jeśli otrzymujesz JSON, a nie określiłeś produces, to może jest to ustawienie domyślne. JSON nie jest jedynym formatem. Na przykład powyższe można łatwo przesłać w XML, ale musiałbyś mieć producesto MediaType.APPLICATION_XML_VALUEi uważam, że musisz skonfigurować HttpMessageConverterdla JAXB. Jeśli chodzi o MappingJacksonHttpMessageConverterskonfigurowany JSON , kiedy mamy Jacksona na ścieżce klas.

Poświęciłbym trochę czasu, aby nauczyć się negocjacji treści . To bardzo ważna część REST. Pomoże Ci to dowiedzieć się o różnych formatach odpowiedzi i jak odwzorować je na swoje metody.

Paul Samsotha
źródło
Jeśli znalazłeś odpowiedź podczas badania, jak utworzyć ogólny / dynamiczny kontroler REST bez użycia @Controller/@RestController. Odkryłem, że muszę jakoś pominąć warstwę resolvera widoku. Nie jest to takie proste, ponieważ klasa AbstractController udostępnia metodę, która musi zwracać nazwę widoku. Zadałem pytanie na ten temat: stackoverflow.com/questions/41016018/… , jeśli masz jakieś pomysły, jak mogę rozwiązać mój problem, napisz komentarz.
nowszy94
1

Ponadto typ zwracania jest określany przez

  1. Czego żąda żądanie HTTP - w nagłówku Accept. Spróbuj spojrzeć na początkowe żądanie, aby zobaczyć, co jest ustawione na Akceptuj.

  2. Co konfiguruje HttpMessageConverters Spring. Spring MVC ustawi konwertery na XML (używając JAXB) i JSON, jeśli biblioteki Jacksona są w ścieżce klas.

Jeśli jest wybór, wybiera jeden - w tym przykładzie jest to JSON.

Jest to omówione w uwagach do kursu. Poszukaj uwag na temat konwerterów komunikatów i negocjacji treści.

paulchapman
źródło