Создание Hypermedia RESTful Web-сервиса
В этом уроке освещается процесс создания "hello world" Hypermedia Driven REST web-сервиса c использованием Spring.
Гипермедиа является важной частью REST. Это позволяет вам создавать сервисы, которые значительно отделяют клиента и сервер, позволяя им развиваться независимо друг от друга. Представления возвращают REST ресурсы, содержащие ссылки, которые указывают на дополнительные ресурсы, с которыми клиент должен взаимодействовать. Поэтому дизайн представления имеет решающее значение в разработке всего сервиса.
Что вы создадите
Вы создадите гипермедиа REST сервис с использованием Spring HATEOAS, библиотекой с API, которую вы можете использовать для создания ссылок к Spring MVC контроллерам, построения представлений ресурсов и управлением тем, как они будут отображены с поддержкой гипермедиа форматов, таких как HAL.
Сервис будет принимать HTTP GET запросы вида:
http://localhost:8080/greeting
А в ответ возвращает JSON представление с сообщением, дополненным простейшим гипермедиа элементом, ссылкой на себя:
{
"content":"Hello, World!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=World"
}
}
}
Ответ уже означает, что вы можете модифицировать сообщение с дополнительным параметром
name
в строке запроса:
http://localhost:8080/greeting?name=User
Параметр name
переопределяет значение по умолчанию "World" и отражается в ответе:
{
"content":"Hello, User!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=User"
}
}
}
Что вам потребуется
- Примерно 15 минут свободного времени
- Любимый текстовый редактор или IDE
- JDK 6 и выше
- Gradle 1.11+ или Maven 3.0+
- Вы также можете импортировать код этого урока, а также просматривать web-страницы прямо из Spring Tool Suite (STS), собственно как и работать дальше из него.
Как проходить этот урок
Как и большинство уроков по Spring, вы можете начать с нуля и выполнять каждый шаг, либо пропустить базовые шаги, которые вам уже знакомы. В любом случае, вы в конечном итоге получите рабочий код.
Чтобы начать с нуля, перейдите в Настройка проекта.
Чтобы пропустить базовые шаги, выполните следующее:
- Загрузите и
распакуйте архив с кодом этого урока, либо кнонируйте из репозитория с помощью
Git:
git clone https://github.com/spring-guides/gs-rest-hateoas.git
- Перейдите в каталог
gs-rest-hateoas/initial
- Забегая вперед, создайте класс представления ресурса
Когда вы закончите, можете сравнить получившийся результат с образцом в gs-rest-hateoas/complete
.
Настройка проекта
Для начала вам необходимо настроить базовый скрипт сборки. Вы можете использовать любую систему сборки, которая вам нравится для сборки проетов Spring, но в этом уроке рассмотрим код для работы с Gradle и Maven. Если вы не знакомы ни с одним из них, ознакомьтесь с соответсвующими уроками Сборка Java-проекта с использованием Gradle или Сборка Java-проекта с использованием Maven.
Создание структуры каталогов
В выбранном вами каталоге проекта создайте следующую структуру каталогов; к примеру,
командой mkdir -p src/main/java/hello
для *nix систем:
└── src └── main └── java └── hello
Создание файла сборки Gradle
Ниже представлен начальный файл сборки Gradle. Файл pom.xml находится здесь. Если вы используете Spring Tool Suite (STS), то можете импортировать урок прямо из него.
pom.xml
, вы найдете, что указана версия для maven-compiler-plugin.
В общем, это не рекомендуется делать. В данном случае он предназначен для решения проблем с нашей CI системы,
которая по умолчанию имеет старую(до Java 5) версию этого плагина.
build.gradle
buildscript {
repositories {
maven { url "http://repo.spring.io/libs-release" }
mavenLocal()
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:1.1.8.RELEASE")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'idea'
apply plugin: 'spring-boot'
jar {
baseName = 'gs-rest-hateoas'
version = '0.1.0'
}
repositories {
mavenLocal()
mavenCentral()
maven { url "http://repo.spring.io/libs-release" }
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web")
compile("com.fasterxml.jackson.core:jackson-databind")
compile("org.springframework.hateoas:spring-hateoas")
compile("org.springframework.plugin:spring-plugin-core:1.1.0.RELEASE")
compile("com.jayway.jsonpath:json-path:0.9.1")
testCompile("org.springframework.boot:spring-boot-starter-test")
}
task wrapper(type: Wrapper) {
gradleVersion = '1.11'
}
Spring Boot gradle plugin предоставляет множество удобных возможностей:
- Он собирает все jar'ы в classpath и собирает единое, исполняемое "über-jar", что делает более удобным выполнение и доставку вашего сервиса
- Он ищет
public static void main()
метод, как признак исполняемого класса - Он предоставляет встроенное разрешение зависимостей, с определенными номерами версий для соответсвующих Spring Boot зависимостей. Вы можете переопределить на любые версии, какие захотите, но он будет по умолчанию для Boot выбранным набором версий
Создание класса представления ресурса
Теперь, когда вы настроили проект, вы можете создать ваш web-сервис.
Начнем с процесса взаимодействия с сервисом.
Сервис будет предоставлять доступ к ресурсу /greeting
через
GET
запросы с необязательным параметром name
в
строке запроса. GET
запрос должен возвращать 200 OK
ответ с JSON в теле представления приветствия.
Помимо этого, JSON представление ресурса будет дополнен списком гипермедиа
элементов в свойстве _links
. Самыми элементарными являются ссылки,
указывающие на самих себя. Представление должно выглядеть примерно так:
{
"content":"Hello, World!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=World"
}
}
}
content
является текстовым представлением приветствия. _links
элемент содержит список ссылок, в данном случае ровно одну с отношением типа rel
и href
атрибутом, указывающим на ресурс.
Моделью представления сообщения является класс представления ресурса, который вы создадите.
Для _links
свойства, фундаментального свойства модели представления, Spring HATEOAS
содержит базовый класс ResourceSupport
, который поддерживает добавление экземпляров
Link
и гарантирует, что они будут отображены как показано выше.
Вы просто создаете POJO, наследуясь от ResourceSupport
и добавляете поле и метод получения
содержимого, а также конструктор с аргументом этого поля:
src/main/java/hello/Greeting.java
package hello;
import org.springframework.hateoas.ResourceSupport;
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
public class Greeting extends ResourceSupport {
private final String content;
@JsonCreator
public Greeting(@JsonProperty("content") String content) {
this.content = content;
}
public String getContent() {
return content;
}
}
- @Jsoncreator - как Jackson может создать экземпляр этого POJO
- @JsonProperty - какое поле Jackson должен поместить в аргумент конструктора
Greeting
в JSON.
Далее вы создадите контроллер ресурса, который будет обрабатывать эти приветствия.
Создание контроллера ресурса
В подходе Spring создания RESTful web-сервисов, HTTP запросы обрабатываются контроллером.
Компоненты просто маркируются @Controller
аннотацией, а GreetingController
обрабатывает GET
запросы для /greeting
, возвращая новый экземпляр
Greeting
класса:
src/main/java/hello/GreetingController.java
package hello;
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.*;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class GreetingController {
private static final String TEMPLATE = "Hello, %s!";
@RequestMapping("/greeting")
@ResponseBody
public HttpEntity<Greeting> greeting(
@RequestParam(value = "name", required = false, defaultValue = "World") String name) {
Greeting greeting = new Greeting(String.format(TEMPLATE, name));
greeting.add(linkTo(methodOn(GreetingController.class).greeting(name)).withSelfRel());
return new ResponseEntity<Greeting>(greeting, HttpStatus.OK);
}
}
Контроллер краток и прост, но там много чего происходит. Давайте подробнее рассмотрим шаг за шагом.
@RequestMapping
аннотация гарантирует, что HTTP запросы к /greeting
будут обработаны greeting()
методом.
GET
, PUT
, POST
и так далее,
потому что @RequestMapping
соответствует всем HTTP операциям по умолчанию.
@RequestMapping(method=GET)
уточняет это соответствие.
@RequestParam
связывает значение строкового параметра name
запроса с
name
параметром метода greeting()
. Этот параметр не required
;
если он отсутствует в запросе, то будет использовано defaultValue
значение "World".
@ResponseBody
аннотация greeting
метода говорит Spring MVC отображать возвращаемый HttpEntity
и его содержимое, Greeting
, напрямую в ответ.
Самое интересной в реализации метода это то, как вы создаете ссылку, указывающую на метод контроллера
и как вы добавляете её в модер представления. Оба linkto(...)
и methodOn(...)
являются статическими методами ControllerLinkBuilder
, который позволяет вам подделать
вызов метода контроллера. Возвращаемый LinkBuilder
проверит метод контроллера на соответствие
аннотации для создания сопоставимого методу URI.
withSelfRel()
создает экземпляр Link
, который вы добавляете в модель
представления Greeting
.
Создание приложения исполняемым
Несмотря на то, что пакет этого сервиса может быть в составе web-приложения и
WAR файлов,
более простой подход, продемонстрированный ниже создает отдельное самостоятельное приложение.
Вы упаковываете все в единый, исполняемый JAR-файл, который запускается через хорошо знакомый
старый main()
Java-метод. Попутно, вы используете поддержку Spring для встроенного
Tomcat
контейнера сервлетов как HTTP среду выполнения вместо развертывания на сторонний экземпляр.
src/main/java/hello/Application.java
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan
@EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Метод main()
передает управление вспомогательному классу
SpringApplication
, предоставляя Application.class
как аргумент его run()
методу. Это говорит Spring о том, чтобы
прочитать аннотацию метаданных из Application
и управлять им
как компонентом в Spring Application Context.
Аннотация @ComponentScan
сообщает Spring о запуске рекурсивного
поиска в пакете hello
и потомках классов, отмеченных прямо или
косвенно Spring аннотацией @Component
. При этом гарантируется,
что Spring найдет и зарегистрирует GreetingController
,
потому что он отмечен @Controller
, что в свою очередь является
своего рода @Component
аннотацией.
@EnableAutoConfiguration
аннотация переключает на доступные по умолчанию настройки, основанные на
содержимом вашего classpath. К примеру, т.к. приложение зависит от встраиваемой
версии Tomcat(tomcat-embed-core.jar), то Tomcat сервер установлен и настроен
по умолчанию от вашего имени. И также, т.к. приложение зависит от Spring MVC
(spring-webmvc.jar), Spring MVC DispatcherServlet
настроен и зарегистрирован за вас - web.xml
не нужен! Поэтому здесь
MultipartConfigElement
, он настроен DispatcherServlet
с
функциональностью загрузки файлов. Автонастройка является мощным и гибким механизмом.
Более подробно вы можете ознакомиться в API документации.
Сборка исполняемого JAR
Вы можете собрать единый исполняемый JAR-файл, который содержит все необходимые зависимости, классы и ресурсы. Это делает его легким в загрузке, версионировании и развертывании сервиса как приложения на протяжении всего периода разработки, на различных средах и так далее.
./gradlew build
Затем вы можете запустить JAR-файл:
java -jar build/libs/gs-rest-hateoas-0.1.0.jar
Если вы используете Maven, вы можете запустить приложение, используя mvn spring-boot:run
,
либо вы можете собрать приложение с mvn clean package
и запустить JAR примерно так:
java -jar target/gs-rest-hateoas-0.1.0.jar
Если вы используете Gradle, вы можете запустить ваш сервис из командной строки:
./gradlew clean build && java -jar build/libs/gs-rest-hateoas-0.1.0.jar
mvn clean package && java -jar target/gs-rest-hateoas-0.1.0.jar
.
Как вариант, вы можете запустить ваш сервис напрямую из Gradle примерно так:
./gradlew bootRun
mvn spring-boot:run
.Данные по логгированию отображаются. Сервис должен быть поднят и запущен через несколько секунд.
Тестирование сервиса
Теперь, когда сервис поднят, зайдите по адресу http://localhost:8080/greeting, где вы увидите:
{
"content":"Hello, World!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=World"
}
}
}
Передайте в запрос строковый параметр name
http://localhost:8080/greeting?name=User.
обратите внимание как значение атрибута content
изменится с "Hello, World!"
на "Hello, User!" и атрибут href
ссылки self
также изменится:
{
"content":"Hello, User!",
"_links":{
"self":{
"href":"http://localhost:8080/greeting?name=User"
}
}
}
Это изменение демонтрирует, что соглашение @RequestParam
в
GreetingController
работает как и ожидалось. Параметру name
дано значение по умолчанию "World", но всегда можно явно переопределить через строку запроса.
Итог
Поздравляем! Вы только что разработали гипермедиа RESTful сервис, используя Spring HATEOAS.
С оригинальным текстом урока вы можете ознакомиться на spring.io.
comments powered by Disqus