Создаем Web-сервисы RESTful при помощи Spring 3
В архитектурном подходе, известном как передача репрезентативного состояния (Representational State Transfer — REST), запросы и ответы строятся вокруг передачи представлений ресурсов. Ресурсы определяются глобальными идентификаторами (ID) — обычно унифицированными идентификаторами ресурсов (Uniform Resource Identifier — URI). Клиентские приложения используют для управления ресурсами или коллекциями ресурсов HTTP-методы (такие как GET, POST, PUT или DELETE). Как правило, метод GET используется для получения ресурса или коллекции ресурсов, POST — для создания, PUT — для обновления или замены, а DELETE — для удаления ресурса.
Например, ответом на запрос GET http://host/context/employees/12345
будет представление сотрудника с идентификатором (ID) 12345. Это ответное представление, содержащее подробную информацию о сотруднике, может быть получено в виде XML или ATOM, а также в виде JSP/HTML-страниц с улучшенным пользовательским интерфейсом. Вид представления будет зависеть от реализации на стороне сервера и MIME-типа запроса клиентов.
Web-сервисы RESTful — это Web-сервисы, реализуемые с использованием HTTP и принципов REST. Как правило, Web-сервис RESTful определяет URI основного ресурса, поддерживаемые MIME-типы представления/ответа и операции.
В этой статье вы узнаете, как использовать Spring для создания Java Web-сервиса RESTful на стороне сервера. В примере используются браузер, утилита curl и Firefox-плагин RESTClient для клиентов, выполняющих запросы. Вы можете загрузить исходный код, используемый в этой статье.
В данной статье предполагается, что вы знакомы с основами REST. В разделе Ресурсы можно найти дополнительную информацию о REST.
Поддержка Spring 3 REST
До появления поддержки REST в Spring для создания Web-сервисов RESTful в Java-приложениях использовалось несколько других реализаций, таких как Restlet, RestEasy и Jersey. Наиболее заметная реализация из этой группы, Jersey, является эталонной реализацией JAX-RS (JSR 311). В разделе Ресурсы можно найти дополнительную информацию о JSR 311 и Jersey.
В Spring, широко распространенной среде Java EE, поддержка создания Web-сервисов RESTful появилась в версии 3 (Release 3). Хотя эта поддержка REST не является реализацией JAX-RS, она имеет больше возможностей, чем определяет спецификация. Поддержка REST полностью интегрирована в MVC-слой Spring и может быть легко использована в приложениях, созданных при помощи Spring.
Основные возможности поддержки REST в Spring:
- Аннотации, такие как
@RequestMapping
и@PathVariable
, для поддержки идентификации ресурсов и отображений URI. ContentNegotiatingViewResolver
для поддержки различных представлений с различными MIME-типами и типами содержимого.- Полная интеграция в исходный MVC-слой с аналогичной моделью программирования.
Создание примера Web-сервиса RESTful
В этом разделе мы шаг за шагом рассмотрим на примере шаги по настройке среды Spring 3 и созданию приложения "Hello World", которое может быть интегрировано в Tomcat. Затем мы перейдем к более сложным приложениям, чтобы представить основные элементы поддержки REST в Spring 3, такие как поддержка разнообразных MIME-типов и поддержка JAXB. Фрагменты кода проиллюстрируют концепции. Вы можете загрузить все примеры кода для этой статьи.
Пример Hello World: использование поддержки REST в Spring 3
Для настройки среды разработки для рассматриваемого примера вам понадобятся:
- IDE: Eclipse IDE for JEE (v3.4+).
- Java SE5 или выше.
- Web-контейнер: Apache Tomcat 6.0 (Jetty или другие, которые тоже будут работать).
- Среда Spring 3 (актуальная версия на момент написания статьи - v3.0.3).
- Другие библиотеки: JAXB 2, JSTL, commons-logging.
Создайте динамическое Web-приложение в Eclipse и настройте его на выполнение в Tomcat 6. Затем нужно настроить файл web.xml, чтобы разрешить Spring WebApplicationContext. В примере конфигурация bean-компонента Spring разделена на два файла: в rest-servlet.xml находятся настройки, относящиеся к MVC/REST, а в rest-context.xml — настройки уровня сервиса (например, bean-компоненты источников данных). В листинге 1 показан фрагмент конфигурации Spring в web.xml.
Листинг 1. Разрешение Spring WebApplicationContext в web.xml
<context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/rest-context.xml </param-value> </context-param> <!-- Этот прослушиватель загрузит файл контекста приложения в дополнение к rest-servlet.xml --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> <servlet> <servlet-name>rest</servlet-name> <servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>rest</servlet-name> <url-pattern>/service/*</url-pattern> </servlet-mapping>
Настройте конфигурацию Spring MVC (Controller, View, View Resolver) в файле rest-servlet.xml.
В листинге 2 показан наиболее важный фрагмент.
<context:component-scan base-package="dw.spring3.rest.controller" /> <!--Чтобы разрешить обработку @RequestMapping на уровне типа и на уровне метода--> <bean class="org.springframework.web.servlet.mvc.annotation .DefaultAnnotationHandlerMapping" /> <bean class="org.springframework.web.servlet.mvc.annotation .AnnotationMethodHandlerAdapter" /> <!--Используйте маршаллер JAXB OXM для маршаллизации/демаршаллизации следующего класса--> <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> <bean id="employees" class= "org.springframework.web.servlet.view.xml.MarshallingView"> <constructor-arg ref="jaxbMarshaller" /> </bean> <bean id="viewResolver" class= "org.springframework.web.servlet.view.BeanNameViewResolver" />
В приведенном выше коде:
Component-scan
- Включает автоматическое сканирование для класса, который имеет аннотации Spring.
На практике он используется для обнаружения аннотации@Controller
, определенной в классах контроллера. DefaultAnnotationHanlderMappings
иAnnotationMethodHandlerAdapter
- Bean-компоненты, выполняющие аннотацию
@ReqeustMapping
класса или метода для обработки средой Spring.
Эта аннотация будет подробно рассмотрена в следующем разделе. Jaxb2Mashaller
- Определяет маршаллер/демаршаллер, который использует JAXB 2 для отображения объекта XML (Object XML Mapping — OXM).
MashallingView
- Определяет XML-представление
view
, которое использует Jaxb2Mashaller. BeanNameViewResolver
- Задает резолвер (определитель) представления на основе имени bean-компонента, которое указывает пользователь.
В примере дляMarshallingView
будет использовано имя "employees" (сотрудники).
На этом завершается конфигурирование Spring. Следующим шагом является написание контроллера, обрабатывающего запрос пользователя. В листинге 3 показан класс контроллера.
Листинг 3. EmployeeController в пакете dw.spring3.rest.controller
@Controller publicclass EmployeeController { @RequestMapping(method=RequestMethod.GET, value="/employee/{id}") public ModelAndView getEmployee(@PathVariable String id) { Employee e = employeeDS.get(Long.parseLong(id)); returnnew ModelAndView(XML_VIEW_NAME, "object", e); } }
Аннотация @RequestMapping
— это ключ к функциональности Spring REST. Она указывает, какой HTTP-метод (RequestMethod.GET
) и какой URI (/employee/{id}
) должны быть обработаны методом с этой аннотацией. Обратите внимание, что:
- Значение, заключенное в
{}
, может быть введено в параметр метода при помощи аннотации@PathVariable
. - XML_VIEW_NAME равно
employees
, а это имя представления, определенное в rest-servlet.xml. employeeDS
– простой источник данных, располагающийся в оперативной памяти (memory-based); его реализация выходит за рамки данной статьи.
Опубликуйте Web-приложение в Tomcat. Теперь можете открыть браузер и ввести http://<host>:<port>/<appcontext>/service/employee/1
. Браузер должен отобразить XML-вид сотрудника с ID, равным 1.
Читайте дальше, чтобы узнать о других возможностях поддержки REST в Spring.
Методы
Управление ресурсами производится при помощи HTTP-методов, таких как GET, POST, PUT и DELETE. Ранее вы узнали, как использовать метод GET
для получения информации о сотрудниках. Теперь мы рассмотрим POST, PUT и DELETE.
При использовании возможностей аннотации @RequestMapping
код для обработки различных методов будет очень похожим. В листинге 4 показан фрагмент кода класса EmployeeController
.
Листинг 4. EmployeeController в dw.spring3.rest.controller
@RequestMapping(method=RequestMethod.POST, value="/employee") public ModelAndView addEmployee(@RequestBody String body) { Source source = new StreamSource(new StringReader(body)); Employee e = (Employee) jaxb2Mashaller.unmarshal(source); employeeDS.add(e); returnnew ModelAndView(XML_VIEW_NAME, "object", e); } @RequestMapping(method=RequestMethod.PUT, value="/employee/{id}") public ModelAndView updateEmployee(@RequestBody String body) { Source source = new StreamSource(new StringReader(body)); Employee e = (Employee) jaxb2Mashaller.unmarshal(source); employeeDS.update(e); returnnew ModelAndView(XML_VIEW_NAME, "object", e); } @RequestMapping(method=RequestMethod.DELETE, value="/employee/{id}") public ModelAndView removeEmployee(@PathVariable String id) { employeeDS.remove(Long.parseLong(id)); List<Employee> employees = employeeDS.getAll(); EmployeeList list = new EmployeeList(employees); returnnew ModelAndView(XML_VIEW_NAME, "employees", list); }
В приведенном выше коде:
- Значение
RequestMethod.<Method>
определяет, какой HTTP-метод должен обрабатывать аннотированный метод. - При помощи
@RequestBody
содержимое тела HTTP-запроса может быть введено в качестве параметра.В примере телом являются XML-данные, которые представляют сотрудника. Мы используем JAXB 2 для демаршаллизации XML в bean-компонент Java, а затем сохраняем его состояние. Пример тела запроса:
<employee><id>3</id><name>guest</name></employee>
- Другие полезные аннотации, которые могут быть введены в параметры метода, — это
@PathVariable
,@RequestParm
и т.д. Полный список аннотаций приведен в документации по Spring (см. Ресурсы).
Коллекция ресурсов
Обычно существует необходимость управлять коллекцией ресурсов - например, если нужно получить информацию обо всех сотрудниках, а не только об одном человеке. Это можно реализовать аналогично предыдущему случаю; нужно всего лишь изменить URI с /employee на /employees. Множественное число для employee семантически соответствует коллекции. В листинге 5 показана реализация.
Листинг 5. GetAllEmployees в EmployeeController
@RequestMapping(method=RequestMethod.GET, value="/employees") public ModelAndView getEmployees() { List<Employee> employees = employeeDS.getAll(); EmployeeList list = new EmployeeList(employees); returnnew ModelAndView(XML_VIEW_NAME, "employees", list); }
Необходимо объявить класс-оболочку для коллекции Employee. Класс-оболочка необходим для JAXB 2, поскольку он не может маршаллизовать класс java.util.List
правильно. В листинге 6 показан класс EmployeeList.
Листинг 6. Класс EmployeeList в dw.spring3.rest.bean
@XmlRootElement(name="employees") publicclass EmployeeList { privateint count; private List<Employee> employees; public EmployeeList() {} public EmployeeList(List<Employee> employees) { this.employees = employees; this.count = employees.size(); } publicint getCount() { return count; } publicvoid setCount(int count) { this.count = count; } @XmlElement(name="employee") public List<Employee> getEmployees() { return employees; } publicvoid setEmployees(List<Employee> employees) { this.employees = employees; } }
Согласование содержимого (content negotiation)
Другой общей чертой REST-сервисов является то, что они могут создавать различные представления в соответствии с запросом. Например, если клиент запрашивает представление всех сотрудников в виде HTML/text, сервер должен создать для пользователя надлежащим образом отформатированную HTML-страницу. Если клиент запрашивает представление всех сотрудников в виде application/XML, сервер должен выдать XML-результат. Другими популярными типами представлений являются ATOM и PDF.
В Spring 3 включен новый резолвер представления, именуемый ContentNegotiatingViewResolver
. Он может переключать резолверы представлений в соответствии с типом содержимого запроса (свойство Accept
в заголовке запроса) или суффиксом URI. В следующем примере ContentNegotiatingViewResolver
используется для реализации поддержки нескольких представлений.
В файле rest-servlet.xml закомментируйте исходный viewResolver
. Вместо него используйте ContentNegotiatingViewResolver
, как показано в листинге 7.
Листинг 7. Определение согласования содержимого
<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> <property name="mediaTypes"> <map> <entry key="xml" value="application/xml"/> <entry key="html" value="text/html"/> </map> </property> <property name="viewResolvers"> <list> <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> <bean class="org.springframework.web.servlet.view.UrlBasedViewResolver"> <property name="viewClass" value= "org.springframework.web.servlet.view.JstlView"/> <property name="prefix" value="/WEB-INF/jsp/"/> <property name="suffix" value=".jsp"/> </bean> </list> </property> </bean>
Определение указывает на поддержку двух типов содержимого запроса: application/xml
и text/html
. Код также определяет два резолвера представления: BeanNameViewResolver
для обработки application/xml и UrlBasedViewResolver
для обработки text/html.
Если в браузере ввести
http://<host>:<port>/<appcontext>/service/employees
, для данных employees будет запрошен формат text/html
. Затем вступит в силу UrlBasedViewResolver
, и Spring в качестве представления выберет /WEB-INF/jsp/employees.jsp. Если добавить в заголовок запроса Accept:application/xml
и выполнить запрос, вступит в силу BeanNameViewResolver
. Согласно коду в листинге 5, он будет использовать представление под названием employees, – то есть представление, определенное в маршаллере JAXB 2.
Код контроллера для GetAllEmployees()
не изменится. Для визуализации страница Employees.jsp будет использовать модель объекта с именем employees
. В листинге 8 показан фрагмент кода для employees.jsp.
Листинг 8. employees.jsp в /WEB-INF/jsp
<table border=1> <thead><tr> <th>ID</th> <th>Name</th> <th>Email</th> </tr></thead> <c:forEach var="employee" items="${employees.employees}"> <tr> <td>${employee.id}</td> <td>${employee.name}</td> <td>${employee.email}</td> </tr> </c:forEach> </table>
Клиенты, взаимодействующие с REST-сервисами
Итак, мы создали простой RESTful Web-сервис, который поддерживает операции CRUD (create — создание, read — чтение, update — обновление, delete — удаление) для данных employees. В этом разделе рассказывается, как взаимодействовать с сервисом. Мы будем тестировать REST-сервисы при помощи curl.
Для тестирования REST-сервисов понадобится Firefox-плагин под названием RESTClient. Он прост в использовании и имеет хороший интерфейс. См. информацию о загрузке в разделе Ресурсы.
Использование curl
Curl - популярная утилита командной строки, позволяющая отправлять запросы на сервер по протоколам HTTP и HTTPS. Это удобный инструмент для взаимодействия с RESTful Web-сервисами, поскольку она позволяет отправлять содержимое при помощи любого HTTP-метода. В Linux® и Mac® Curl является встроенной. Для платформы Windows® ее можно загрузить (см. Ресурсы).
Чтобы выполнить первую команду curl для получения всех сотрудников, введите:
curl –HAccept:application/xml http://localhost:8080/rest/service/employees
Ответ в XML-представлении будет содержать всех сотрудников, как показано на рисунке 1.
Рисунок 1. XML-представление всех сотрудников
Кликните, чтобы увидеть увеличенное изображение
Также можно ввести этот URL в браузере. В данном случае параметр Accept заголовка устанавливается в text/html, так что отобразится таблица, определенная в employees.jsp. На рисунке 2 показан пример.
Рисунок 2. XML-представление всех сотрудников
Кликните, чтобы увидеть увеличенное изображение
Для добавления нового сотрудника на сервер при помощи метода POST используйте приведенный ниже код. Код addEmployee()
в листинге 4 возьмет тело запроса и демаршаллизует его в объект Employee
.
curl -X POST -HContent-type:application/xml --data "<employee><id>3</id><name>guest</name><email>guest@ibm.com</employee>" http://localhost:8080/rest/service/employee
Новый сотрудник добавлен. Можно проверить список сотрудников, повторив первый пример.
Метод PUT похож на метод POST.
curl -X PUT -HContent-type:application/xml --data "<employee><id>3</id><name>guest3</name><email>guest3@ibm.com</employee>" http://localhost:8080/rest/service/employee/3
Этот код обновляет данные сотрудника с идентификатором 3.
Резюме
Теперь, когда инфраструктура Spring 3 поддерживает REST на своем MVC-уровне, можно использовать интерфейсы и аннотации Spring для создания Web-сервисов RESTful-стиля. В этой статье на примерах показано, как с помощью новых возможностей Spring 3 можно легко создавать Java RESTful Web-сервисы на стороне сервера.
Ресурсы для скачивания
- этот контент в PDF
- Исходный код статьи (src.zip | 9 КБ)
Похожие темы
- Оригинал статьи Build RESTful web services using Spring 3 (EN).
- Вводную информацию и ссылки на другие относящиеся к REST ссылки можно найти в Википедии.
- Узнайте все о Spring 3.
- Jersey: материалы для загрузки, архивы с примерами кода, руководство пользователя и документация по JAX-RS API. Jersey — это пригодная для производственного применения эталонная реализация с открытым исходным кодом (под двойной лицензией CDDL+GPL) JAX-RS (JSR 311) для создания Web-сервисов RESTful.
- В статье Создание Web-сервиса RESTful при помощи Jersey и Apache Tomcat (DeveloperWorks, сентябрь 2009 года), написанной авторами данной статьи, рассказывается, как плавно перейти от сервиса в стиле сервлета к RESTful-сервису при помощи интеграции Jersey в Apache Tomcat (EN).
- Узнайте больше о проекте JAXB Reference Implementation Project.
- Spring 3: материалы для загрузки, документы и учебники.
- curl для Windows.
- Плагин для Firefox RESTClient, который используется для посещения и тестирования сервисов RESTful/WebDAV.
- Загрузите ознакомительные версии продуктов IBM или исследуйте их в интерактивном режиме в IBM SOA Sandbox, чтобы оценить возможности инструментов разработки приложений и продуктов промежуточного уровня семейств DB2®, Lotus®, Rational®, Tivoli® и WebSphere®.