Как работает аннотация Spring @ResponseBody?

89

У меня есть метод, который аннотируется следующим образом:

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

Итак, я знаю это по этой аннотации:

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

этот метод обрабатывает запросы GET HTTP к ресурсу, представленному URL / orders .

Этот метод вызывает объект DAO, который возвращает список .

где Account представляет пользователя в системе и имеет некоторые поля, которые представляют этого пользователя, например:

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

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

У меня вопрос: как именно работает @ResponseBodyаннотация?

Он расположен перед возвращаемым List<Account>объектом, поэтому я думаю, что он относится к этому списку. В документации курса указано, что эта аннотация выполняет следующие функции:

убедитесь, что результат будет записан в ответ HTTP конвертером сообщений HTTP (вместо представления MVC).

А также прочтите официальную документацию Spring: http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/ResponseBody.html

кажется, что он берет List<Account>объект и помещает его в Http Response. Это правильно или я неправильно понимаю?

В комментарии к предыдущему accountSummary()способу записано :

Вы должны получить результаты JSON при доступе к http: // localhost: 8080 / rest-ws / app / accounts.

Так что именно это означает? Означает ли это, что List<Account>объект, возвращаемый accountSummary()методом, автоматически преобразуется в JSONформат, а затем помещается в формат Http Response? Или что?

Если это утверждение верно, где указано, что объект будет автоматически преобразован в JSONформат? @ResponseBodyПрименяется ли стандартный формат при использовании аннотации или он указывается где-то еще?

Андреа Нобили
источник

Ответы:

160

Во-первых, аннотация не аннотирует List. Он аннотирует метод, как и RequestMappingделает. Ваш код эквивалентен

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

Теперь аннотация означает, что возвращаемое значение метода будет составлять тело ответа HTTP. Конечно, ответ HTTP не может содержать объекты Java. Таким образом, этот список учетных записей преобразуется в формат, подходящий для приложений REST, обычно JSON или XML.

Выбор формата зависит от установленных конверторов сообщений, на значении producesатрибута в @RequestMappingаннотации, а также от типа контента, клиент принимает (который доступен в заголовках запроса HTTP). Например, если в запросе указано, что он принимает XML, но не JSON, и установлен конвертер сообщений, который может преобразовать список в XML, будет возвращен XML.

JB Nizet
источник
3
привет, как нам настроить «установленные конвертеры сообщений»? Я хочу, чтобы по умолчанию всегда конвертировался в Json. Могу ли я это сделать?
Sam YC
@JB Nizet, не могли бы вы объяснить, почему HTTP-ответ не может содержать java-объекты. Я новичок в java.
Naved Ali 06
3
@ Er.NavedAli прочтите спецификацию http, тип содержимого ответа http определяет допустимое содержимое, которое может содержать ответ http. допустимые значения для этого могут быть "application / octet-stream", "image / jpeg", "text / HTML", но объекты java не являются допустимым значением для этого.
ZhaoGang
@ZhaoGang, Content-type application / json в моем тестовом примере заменен на application / xml, но тело ответа, похоже, не изменилось, все еще присутствует формат json.
LeafiWan
1
где именно, я имею в виду, в каком классе пружины принимается решение определить, как вернуть ответ? Можете ли вы указать мне источник на github, где делается это решение или имя класса? И что произойдет, если клиент принимает XML, но конвертер XML не установлен?
anir 01
66

Первое, что нужно понять, - это разница в архитектурах.

На одном конце у вас есть архитектура MVC, которая основана на вашем обычном веб-приложении, использующем веб-страницы, и браузер делает запрос страницы:

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

Браузер делает запрос, контроллер (@Controller) получает модель (@Entity) и создает представление (JSP) из модели, и представление возвращается обратно клиенту. Это базовая архитектура веб-приложения.

С другой стороны, у вас есть архитектура RESTful. В этом случае View отсутствует. Контроллер отправляет обратно только модель (или представление ресурса, в более терминах RESTful). Клиент может быть приложением JavaScript, серверным приложением Java, любым приложением, в котором мы предоставляем наш REST API. В этой архитектуре клиент решает, что делать с этой моделью. Возьмем, к примеру, Twitter. Twitter как веб-(REST) ​​API, который позволяет нашим приложениям использовать его API для получения таких вещей, как обновления статуса, чтобы мы могли использовать его для размещения этих данных в нашем приложении. Эти данные будут иметь некоторый формат, например JSON.

При этом при работе со Spring MVC он был сначала построен для обработки базовой архитектуры веб-приложений. Существуют разные разновидности сигнатур методов, которые позволяют создавать представление из наших методов. Метод может возвращать a ModelAndViewтам, где мы его явно создаем, или есть неявные способы, где мы можем вернуть некоторый произвольный объект, который устанавливается в атрибуты модели. Но в любом случае где-то в цикле запрос-ответ будет создано представление.

Но когда мы используем @ResponseBody, мы говорим, что не хотим создавать представление. Мы просто хотим отправить возвращаемый объект как тело в любом указанном нами формате. Мы бы не хотели, чтобы это был сериализованный объект Java (хотя это возможно). Так что да, его нужно преобразовать в какой-то другой общий тип (с этим типом обычно разбираются посредством согласования содержимого - см. Ссылку ниже). Честно говоря, я мало работаю со Spring, хотя иногда балуюсь ею. Обычно я использую

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

для установки типа содержимого, но, возможно, по умолчанию используется JSON. Не цитируйте меня, но если вы получаете JSON и не указали его produces, возможно, это значение по умолчанию. JSON - не единственный формат. Например, указанное легко может быть отправлен в XML, но вы должны иметь , producesчтобы MediaType.APPLICATION_XML_VALUEи я считаю , что вам нужно настроить HttpMessageConverterдля JAXB. Что касается MappingJacksonHttpMessageConverterнастроенного JSON , когда у нас есть Джексон в пути к классам.

Мне нужно время, чтобы узнать о Content Negotiation . Это очень важная часть REST. Это поможет вам узнать о различных форматах ответов и о том, как сопоставить их с вашими методами.

Пол Самсота
источник
Если вы нашли свой ответ при исследовании того, как создать общий / динамический контроллер REST без использования @Controller/@RestController. Я обнаружил, что мне нужно как-то пропустить слой преобразователя представлений. Это не так просто, потому что класс AbstractController предоставляет метод, который должен возвращать имя представления. Я задал вопрос по этому поводу: stackoverflow.com/questions/41016018/… , если у вас есть идеи о том, как я могу решить мою проблему, оставьте комментарий.
nowszy94 07
1

Кроме того, тип возвращаемого значения определяется

  1. То, что HTTP-запрос говорит, что он хочет - в заголовке Accept. Попробуйте взглянуть на первоначальный запрос, чтобы узнать, что установлено на Accept.

  2. Что настраивает HttpMessageConverters Spring. Spring MVC настроит преобразователи для XML (с использованием JAXB) и JSON, если библиотеки Джексона находятся в пути к классам.

Если есть выбор, он выбирает один - в этом примере это JSON.

Это будет покрыто курсом нот. Ищите примечания о преобразователях сообщений и согласовании содержимого.

Paulchapman
источник