В предыдущей статье "Создаем Web-сервисы RESTful при помощи Spring 3" (см. Ресурсы) был представлен способ создания Web-сервисов RESTful с помощью Sping. В ней также объяснялось, как использовать ContentNegotiatingViewResolver для создания нескольких представлений, что является важной функцией Web-сервисов RESTful. В данной статье описывается еще один способ создания нескольких представлений при помощи HttpMessageConverter, а также приводятся примеры использования RestTemplate с HttpMessageConverter для взаимодействия с сервисами.
В этом разделе представлен обзор основных функций Spring (или аннотаций), поддерживающих Web-сервисы RESTful.
@Controller- Используйте аннотацию
@Controller, чтобы проаннотировать класс, который будет контроллером в MVC и обработает HTTP-запрос. @RequestMapping- Используйте аннотацию
@RequestMapping, чтобы проаннотировать функцию, которая должна обработать определенные HTTP-методы, URI или HTTP-заголовки. Эта аннотация является ключом к поддержке REST в Spring. Вы можете изменить параметрmethodдля обработки других HTTP-методов.Например:
@RequestMapping(method=RequestMethod.GET, value="/emps", headers="Accept=application/xml, application/json") @PathVariable- Аннотация
@PathVariableпозволяет вводить в URI переменную пути в качестве параметра.Например:
@RequestMapping(method=RequestMethod.GET, value="/emp/{id}") public ModelAndView getEmployee(@PathVariable String id) { … }
- Другие полезные аннотации
- Используйте
@RequestParam, чтобы ввести URL-параметр в метод.Используйте
@RequestHeader, чтобы ввести определенный HTTP-заголовок в метод.Используйте
@RequestBody, чтобы ввести тело HTTP-запроса в метод.Используйте
@ResponseBody, чтобы вернуть содержимое или объект в качестве тела HTTP-ответа.Используйте
HttpEntity<T>для автоматического инъецирования в метод, если вы предоставляете его в качестве параметра.Используйте
ResponseEntity<T>, чтобы вернуть HTTP-ответ с пользовательским статусом или заголовками.Например:
public @ResponseBody Employee getEmployeeBy(@RequestParam("name") String name, @RequestHeader("Accept") String accept, @RequestBody String body) {…} public ResponseEntity<String> method(HttpEntity<String> entity) {…}
В документации по Spring (см. Ресурсы) приведен полный список поддерживаемых аннотации или объектов, которые могут быть введены в метод.
Поддержка нескольких представлений
Представление одного и того же ресурса с различными MIME-типами является важным свойством RESTful Web-сервисов. Как правило, можно использовать один и тот же URI с различными HTTP-заголовками приема для извлечения ресурса в различных представлениях. Можно также использовать различные URI или URI с различными параметрами запроса.
В статье "Создаем Web-сервисы RESTful при помощи Spring 3" (см. Ресурсы) представлен ContentNegotiatingViewResolver, который может выбирать различные резолверы представления для обработки одного и того же URI (с отличающимся заголовком приема). Таким образом, ContentNegotiatingViewResolver может быть использован для создания нескольких представлений.
Существует и другой способ создания нескольких представлений — комбинация HttpMessageConverter и аннотации @ResponseBody. В этом случае не нужно использовать технологии View.
HTTP-запросы и ответы являются текстовыми, т.е. браузер и сервер взаимодействуют путем обмена необработанными текстами. Однако в Spring методы в классе контроллера возвращают чистый тип String и модели домена (или другие встроенные Java-объекты). Как Spring сериализует/десериализует объекты в необработанные тексты? Это делает HttpMessageConverter. Spring содержит реализации, которые могут удовлетворить ваши обычные потребности. В таблице 1 приведены некоторые примеры.
Таблица 1. Примеры HttpMessageConverter
| При помощи... | Можно... |
|---|---|
| StringHttpMessageConverter | Прочитать/записать строку в запросе и ответе. По умолчанию он поддерживает медиа-тип text/* и осуществляет запись с заголовком Content-Type, установленным в text/plain. |
| FormHttpMessageConverter | Прочитать/записать данные формы в запросе и ответе. По умолчанию он читает медиа-тип pplication/x-www-form-urlencoded и записывает данные в MultiValueMap<String,String>. |
| MarshallingHttpMessageConverter | Прочитать/записать XML-данные при помощи маршаллера/демаршаллера Spring. Он преобразует данные с медиа-типом application/xml. |
| MappingJacksonHttpMessageConverter | Прочитать/записать JSON-данные при помощи Jackson's ObjectMapper. Он преобразует данные с медиа-типом application/json. |
| AtomFeedHttpMessageConverter | Прочитать/записать фид ATOM при помощи ROME Feed API. Он преобразует данные с медиа-типом application/atom+xml. |
| RssChannelHttpMessageConverter | Прочитать/записать фид RSS при помощи ROME Feed API. Он преобразует данные с медиа-типом application/rss+xml. |
В этом разделе рассказывается, как разработать простой Web-сервис RESTful, который может создавать несколько представлений. Частично исходный код, используемый в примере приложения, был разработан в статье "Создавайте Web-сервисы RESTful при помощи Spring 3" (см. Ресурсы). Вы также можете загрузить исходный код.
Во-первых, необходимо настроить HttpMessageConverter. Чтобы создать несколько представлений, настройте несколько экземпляров HttpMessageConverter для преобразования объекта в различные медиа-типы. В этом разделе рассматриваются медиа-типы JSON, ATOM и XML.
Начнем с простейшего примера. JSON - это облегченный формат обмена данными, с которым легко работать человеку. В листинге 1 показан код, который настраивает конвертор JSON.
Листинг 1. Настройка HttpMessageConverter в rest-servlet.xml
<bean class="org.springframework.web.servlet.mvc.annotation
.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonConverter" />
<ref bean="marshallingConverter" />
<ref bean="atomConverter" />
</list>
</property>
</bean>
<bean id="jsonConverter"
class="org.springframework.http.converter.json
.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes" value="application/json" />
</bean> |
В данной конфигурации зарегистрированы три конвертора. MappingJacksonHttpMessageConverter используется для преобразования объекта в JSON и наоборот. Этот встроенный конвертер использует Jackson ObjectMapper для отображения JSON в JavaBean, поэтому необходимо добавить следующие Jackson JAR-файлы в classpath.
- org.codehaus.jackson.jar
- org.codehaus.jackson.mapper.jar
Следующим шагом будет написание метода для обработки запроса представления JSON. В листинг 2 показаны детали.
Листинг 2. Обработка JSON-запросов, определенных в EmployeeController
@RequestMapping(method=RequestMethod.GET, value="/emp/{id}",
headers="Accept=application/json")
public @ResponseBody Employee getEmp(@PathVariable String id) {
Employee e = employeeDS.get(Long.parseLong(id));
return e;
}
@RequestMapping(method=RequestMethod.GET, value="/emps",
headers="Accept=application/json")
public @ResponseBody EmployeeListinggetAllEmp() {
List<Employee> employees = employeeDS.getAll();
EmployeeListinglist = new EmployeeList(employees);
return list;
}
|
Аннотация @ResponseBody используется для того, чтобы сделать возвращаемый объект (Employee или EmployeeList) содержимым тела ответа, который MappingJacksonHttpMessageConverter отобразит в JSON.
При использовании HttpMessageConverter и @ResponseBody можно реализовать несколько представлений, не применяя технологию представлений Spring View, что является преимуществом по сравнению с использованием ContentNegotiatingViewResolver.
Теперь можно использовать CURL или плагин REST-клиента для Firefox для вызова запроса. Не забудьте добавить HTTP-заголовок: Accept=application/json. В листинге 3 показан требуемый ответ в формате JSON.
Листинг 3. JSON-результат для getEmp() и getAllEmp()
Response for /rest/service/emp/1
{"id":1,"name":"Huang Yi Ming","email":"huangyim@cn.ibm.com"}
Response for /rest/service/emps
{"count":2,
"employees":[
{"id":1,"name":"Huang Yi Ming","email":"huangyim@cn.ibm.com"},
{"id":2,"name":"Wu Dong Fei","email":"wudongf@cn.ibm.com"}
]}
|
Встроенный в Spring конвертер MarshallingHttpMessageConverter используется для отображения между объектом и XML (OXM). В следующем примере JAXB 2 используется в качестве маршаллера/демаршаллера для конвертера. В листинге 4 показана настройка.
Листинг 4. Настройка MarshallingHttpMessageConverter
<bean id="marshallingConverter"
class="org.springframework.http.converter.xml
.MarshallingHttpMessageConverter">
<constructor-arg ref="jaxbMarshaller" />
<property name="supportedMediaTypes" value="application/xml"/>
</bean>
<bean id="jaxbMarshaller"
class="org.springframework.oxm.jaxb.Jaxb2Marshaller">
<property name="classesToBeBound">
<list>
<value>dw.spring3.rest.bean.Employee</value>
<value>dw.spring3.rest.bean.EmployeeList</value>
</list>
</property>
</bean>
|
Важно понимать, что в JAXB 2 нет хорошей поддержки отображения java.util.List<T> в XML. Распространенной практикой является добавление класса-оболочки для коллекции объектов. Обратитесь к статье "Создавайте Web-сервисы RESTful при помощи Spring 3" (см. Ресурсы) либо загрузите исходный код, чтобы получить подробную информацию об этом аннотированном JAXB-классе.
А как насчет метода в контроллере, который обрабатывает запрос? Вернитесь к коду в листинге 2. Неудивительно, что нет необходимости добавлять здесь код. Просто нужно добавить еще один поддерживаемый медиа-тип в заголовке Accept:
headers=”Accept=application/json, application/xml”
|
Конвертер должным образом отобразит объект в запрашиваемый тип (JSON или XML). В листинге 5 показан требуемый результат при запросе представления application/xml.
Листинг 5. XML-результат для getEmp() и getAllEmp()
Response for /rest/service/emp/1
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employee>
<email>huangyim@cn.ibm.com</email>
<id>1</id>
<name>Huang Yi Ming</name>
</employee>
Response for /rest/service/emps
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<employees>
<count>2</count>
<employee>
<email>huangyim@cn.ibm.com</email>
<id>1</id>
<name>Huang Yi Ming</name>
</employee>
<employee>
<email>wudongf@cn.ibm.com</email>
<id>2</id><name>Wu Dong Fei</name>
</employee>
</employees>
|
Фид ATOM - еще один популярный формат обмена данными в Web-сервисах RESTful. Документ фида Atom является представлением фида Atom, включая метаданные о фиде и некоторые (или все) записи, связанные с ним. Его основным элементом является atom:feed. Существует также протокол ATOM Publish Protocol (APP), определяющий формат обмена и режимы работы. (Определение ATOM и APP-форматов выходит за рамки данной статьи. См. ссылки на дополнительную информацию в разделе Ресурсы.)
В примере применен AtomFeedHttpMessageConverter для преобразования фида ATOM, использующего ROME ATOM API. Поэтому необходимо включить JAR-файл sun.syndication.jar в classpath. В листинге 6 показана настройка этого конвертера.
Листинг 6. Настройка AtomFeedHttpMessageConverter
<bean id="atomConverter"
class="org.springframework.http.converter.feed
.AtomFeedHttpMessageConverter">
<property name="supportedMediaTypes" value="application/atom+xml" />
</bean>
|
В листинге 7 показан код, который обрабатывает запрос ATOM и фид.
Листинг 7. getEmpFeed() в классе EmployeeController & AtomUtil
@RequestMapping(method=RequestMethod.GET, value="/emps",
headers="Accept=application/atom+xml")
public @ResponseBody Feed getEmpFeed() {
List<Employee> employees = employeeDS.getAll();
return AtomUtil.employeeFeed(employees, jaxb2Mashaller);
}
public static Feed employeeFeed(
List<Employee> employees, Jaxb2Marshaller marshaller) {
Feed feed = new Feed();
feed.setFeedType("atom_1.0");
feed.setTitle("Employee Atom Feed");
List<Entry> entries = new ArrayList<Entry>();
for(Employee e : employees) {
StreamResult result = new StreamResult(
new ByteArrayOutputStream());
marshaller.marshal(e, result);
String xml = result.getOutputStream().toString();
Entry entry = new Entry();
entry.setId(Long.valueOf(e.getId()).toString());
entry.setTitle(e.getName());
Content content = new Content();
content.setType(Content.XML);
content.setValue(xml);
List<Content> contents = new ArrayList<Content>();
contents.add(content);
entry.setContents(contents);
entries.add(entry);
}
feed.setEntries(entries);
return feed;
}
|
Обратите внимание, что в приведенном выше коде:
- Метод
GetEmpFeed()обрабатывает тот же URI, что иgetAllEmp(), но с различными заголовкамиAccept. - При помощи метода
employeeFeed()объектEmployeeмаршаллизуется в XML, а затем добавляется в элемент<content>фида.
В листинге 8 показан результат при запросе представления application/atom+xml для URI /rest/service/emps.
Листинг 8. Результат для /rest/service/emps при запросе application/atom+xml
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Employee Atom Feed</title>
<entry>
<title>Huang Yi Ming</title>
<id>1</id>
<content type="xml">
<employee>
<email>huangyim@cn.ibm.com</email>
<id>1</id>
<name>Huang Yi Ming</name>
</employee>
</content>
</entry>
<entry>
<title>Wu Dong Fei</title>
<id>2</id>
<content type="xml">
<employee>
<email>wudongf@cn.ibm.com</email>
<id>2</id>
<name>Wu Dong Fei</name>
</employee>
</content>
</entry>
</feed>
|
Ранее в примерах были реализованы методы для обработки HTTP-метода GET. В листинге 9 приведена реализация методов POST, PUT и DELETE.
Листинг 9. Методы POST, PUT и DELETE в EmployeeController
@RequestMapping(method=RequestMethod.POST, value="/emp")
public @ResponseBody Employee addEmp(@RequestBody Employee e) {
employeeDS.add(e);
return e;
}
@RequestMapping(method=RequestMethod.PUT, value="/emp/{id}")
public @ResponseBody Employee updateEmp(
@RequestBody Employee e, @PathVariable String id) {
employeeDS.update(e);
return e;
}
@RequestMapping(method=RequestMethod.DELETE, value="/emp/{id}")
public @ResponseBody void removeEmp(@PathVariable String id) {
employeeDS.remove(Long.parseLong(id));
}
|
Аннотация @RequestBody используется в методах addEmp() и updateEmp(). Она принимает тело HTTP-запроса и пытается преобразовать его в класс объекта, используя зарегистрированный HttpMessageConverter. В следующем разделе мы используем RestTemplate для взаимодействия с REST-сервисами.
Использование RestTemplate для взаимодействия с REST-сервисами
В статье "Создаем Web-сервисы RESTful при помощи Spring 3" (см. Ресурсы) рассказывалось, как использовать CURL и REST-клиент для тестирования REST-сервисов. Для этого на уровне программирования обычно используется Jakarta Commons HttpClient (но эта тема выходит за рамки данной статьи). Также может быть использован Spring REST-клиент под названием RestTemplate. Он концептуально похож на другие шаблонные классы Spring, такие как JdbcTemplate и JmsTemplate.
RestTemplate также использует HttpMessageConverter. Вы можете передать класс объекта в запросе и дать возможность конвертеру обработать отображения.
В листинге 10 показана настройка RestTemplate. Также используются три представленных ранее конвертера.
Листинг 10. Настройка RestTemplate
<bean id="restTemplate"
class="org.springframework.web.client.RestTemplate">
<property name="messageConverters">
<list>
<ref bean="marshallingConverter" />
<ref bean="atomConverter" />
<ref bean="jsonConverter" />
</list>
</property>
</bean>
|
В примерах этой статьи использовались только некоторые из методов, позволяющие упростить взаимодействие между серверами. RestTemplate поддерживает и другие методы, в том числе:
-
exchange: выполняет определенные HTTP-методы с телом запроса и получает ответ. -
getForObject: выполняет HTTP-методGETи получает ответ в виде объекта. -
postForObject: выполняет HTTP-методPOSTс определенным телом запроса. -
put: выполняет HTTP-методPUTс определенным телом запроса. -
delete: выполняет HTTP-методDELETEдля определенных URI.
Следующие примеры кода демонстрируют использование RestTemplate. Обратитесь к RestTemplate API (см. Ресурсы), чтобы получить детальную информацию по используемым API.
В листинге 11 показано, как добавить заголовки в запрос, а затем выполнить его. Используя MarshallingHttpMessageConverter, можно получить ответ и преобразовать его в типизированный класс. Для проверки других представлений можно использовать различные медиа-типы.
Листинг 11. Запрос XML-представления
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
HttpEntity<String> entity = new HttpEntity<String>(headers);
ResponseEntity<EmployeeList> response = restTemplate.exchange(
"http://localhost:8080/rest/service/emps",
HttpMethod.GET, entity, EmployeeList.class);
EmployeeListingemployees = response.getBody();
// обработать employees
|
В листинге 12 показано добавление нового сотрудника на сервер при помощи метода POST. Сервис AddEmp() на стороне сервера может принимать данные с медиа-типами application/xml и application/json.
Листинг 12. Добавление нового сотрудника при помощи метода POST
Employee newEmp = new Employee(99, "guest", "guest@ibm.com");
HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp);
ResponseEntity<Employee> response = restTemplate.postForEntity(
"http://localhost:8080/rest/service/emp", entity, Employee.class);
Employee e = response.getBody();
// обработать employee
|
В листинге 13 показано, как модифицировать сотрудника при помощи метода PUT. Также демонстрируется возможность использования символа-заполнителя ({id}) в URI запроса.
Листинг 13. Метод PUT для обновления сотрудника
Employee newEmp = new Employee(99, "guest99", "guest99@ibm.com");
HttpEntity<Employee> entity = new HttpEntity<Employee>(newEmp);
restTemplate.put(
"http://localhost:8080/rest/service/emp/{id}", entity, "99");
|
В листинге 14 показано, как удалить существующего сотрудника при помощи метода DELETE.
Листинг 14. Метод DELETE для удаления сотрудника
restTemplate.delete(
"http://localhost:8080/rest/service/emp/{id}", "99");
|
В этой статье вы узнали о HttpMessageConverter, появившемся в Spring 3. Он предоставляет поддержку нескольких представлений как на стороне клиента, так и на стороне сервера. Используя предоставленный исходный код, можно исследовать различия между реализацией HttpMessageConverter рассмотренной в данной статье, и реализацией с использованием ContentNegotiatingViewResolver рассмотренной в статье "Создаем Web-сервисы RESTful при помощи Spring 3".
| Описание | Имя | Размер | Метод загрузки |
|---|---|---|---|
| Исходный код для статьи | src_code.zip | 11 КБ | HTTP |
Научиться
- Оригинал статьи Build RESTful web services with the Spring 3 MVC HttpMessageConverter feature (EN).
- В предыдущей статье Создаем Web-сервисы RESTful при помощи Spring 3 (developerWorks, июль 2010 года, EN) был представлен способ создания Web-сервисов RESTful с использованием Spring.
- Вводную информацию и ссылки на другие относящиеся к REST материалы можно найти в Википедии.
- Познакомьтесь с демонстрацией Spring MVC Showcase, чтобы получить полное представление о том, что может эта технология. В нее включен пример проекта, а также слайдовая презентация и скринкаст.
- Узнайте все о Spring 3.
- Дополнительная информация об интерфейсах RestTemplate API.
- Узнайте больше о проекте JAXB Reference Implementation Project.
- Исследуйте ROME - набор Java-утилит Atom/RSS, который облегчает работу в Java с большинством форматов синдикации.
- Узнайте больше об ATOM.
- Прочтите на DeveloperWorks серию статей Знакомство с Atom Publishing Protocol (октябрь 2006 г.), где приведен общий обзор этого протокола, его назначения и возможностей (EN).
- Познакомьтесь с Jackson - быстрым JSON-процессором с открытым исходным кодом.
Получить продукты и технологии
- Последние версии Spring.
- Загрузите Jackson.
- Загрузите ознакомительные версии продуктов IBM или исследуйте их в интерактивном режиме в IBM SOA Sandbox, чтобы оценить возможности инструментов разработки приложений и продуктов промежуточного уровня семейств DB2®, Lotus®, Rational®,
Tivoli® и WebSphere®.
Обсудить
- Найдите других членов developerWorks, интересующихся Web-разработкой.
- Web-разработчики могут поделиться своим опытом и знаниями в группе Web-разработки.
- Роланд Барсия рассказывает в своем блоге о Web 2.0 и программном обеспечении промежуточного уровня.
- Используйте общие закладки на Web-темы, созданные членами developerWorks.
