Создаем Web-сервисы RESTful-стиля при помощи функции Spring 3 MVC HttpMessageConverter

Spring, популярная среда для создания приложений на Java™ Platform Enterprise Edition (Java EE), теперь поддерживает передачу репрезентативного состояния (Representational State Transfer — REST) на уровне модель-представление-контроллер (Model-View-Controller — MVC). Для Web-сервисов RESTful важно создавать несколько представлений, основанных на запросах клиента. В этой статье вы узнаете, как создать несколько представлений при помощи HttpMessageConverter. В примерах кода показано, как использовать RestTemplate с HttpMessageConverter для взаимодействия с сервисами. Кроме того, вы узнаете, как использовать интерфейсы и аннотации Spring при разработке Web-сервисов RESTful-стиля, создающих такие популярные представления, как ATOM Feed, XML и JavaScript Object Notation (JSON). Введение

И Мин Хуан, инженер-программист, IBM

И Мин Хуан (Yi Ming Huang) - фотографияИ Мин Хуан (Yi Ming Huang) - инженер-программист China Development Lab, занимается Mashup-приложениями в Lotus. Он имеет опыт Web-разработки на базе портлетов и виджетов. Интересуется технологиями REST, OSGi и Spring.



04.07.2011

Введение

В предыдущей статье "Создаем Web-сервисы RESTful при помощи Spring 3" (см. Ресурсы) был представлен способ создания Web-сервисов RESTful с помощью Sping. В ней также объяснялось, как использовать ContentNegotiatingViewResolver для создания нескольких представлений, что является важной функцией Web-сервисов RESTful. В данной статье описывается еще один способ создания нескольких представлений при помощи HttpMessageConverter, а также приводятся примеры использования RestTemplate с HttpMessageConverter для взаимодействия с сервисами.


Поддержка REST в Spring MVC

В этом разделе представлен обзор основных функций 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.


HttpMessageConverter

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, который может создавать несколько представлений. Частично исходный код, используемый в примере приложения, был разработан в статье "Создавайте Web-сервисы RESTful при помощи Spring 3" (см. Ресурсы). Вы также можете загрузить исходный код.

Во-первых, необходимо настроить HttpMessageConverter. Чтобы создать несколько представлений, настройте несколько экземпляров HttpMessageConverter для преобразования объекта в различные медиа-типы. В этом разделе рассматриваются медиа-типы JSON, ATOM и XML.

JSON

Начнем с простейшего примера. 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"}
]}

XML

Встроенный в 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

Фид 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>

Реализация POST, PUT и DELETE

Ранее в примерах были реализованы методы для обработки 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. Вы можете передать класс объекта в запросе и дать возможность конвертеру обработать отображения.

Настройка RestTemplate

В листинге 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.zip11 КБ

Ресурсы

Научиться

  • Оригинал статьи 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-процессором с открытым исходным кодом.

Получить продукты и технологии

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=SOA и web-сервисы, Web-архитектура
ArticleID=696963
ArticleTitle=Создаем Web-сервисы RESTful-стиля при помощи функции Spring 3 MVC HttpMessageConverter
publish-date=07042011