# 1. Клиент-серверная архитектура. Создание простой RESTful веб-службы с помощью Spring Boot.

### Клиент-серверная архитектура

Сервер - компьютер или программа, которая управляет ресурсами (информация, файлы, база данных) называется сервером этого ресурса или просто сервером.

Архитектура "клиент-сервер" определяет общие принципы организации взаимодействия, где имеются серверы (узлы-поставщики некоторых специфичных функций и сервисов) и клиенты, (потребители этих сервисов).

Между клиентами и серверами должны быть установлены правила взаимодействия, которые называются протоколом взаимодействия или протоколом обмена. Каждая часть взаимодействует друг с другом, обмениваясь сообщениями в заранее согласованном формате.

{% hint style="info" %}
Более подробно про клиент-серверное взаимодействие читайте здесь - <http://bit.ly/2qmKbHk>
{% endhint %}

В рамках данного курса рассматривается так называемая "трехзвенная архитектура"

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-LtYAOdkAcnChZflt_QB%2F-LtYCdWC7V2S8XZsQ3q8%2Fimage.png?alt=media\&token=c332fc00-1ba6-476f-8f5a-9959d519e9f7)

Компоненты трехзвенной архитектуры:

* клиент - этот компонент отвечает за представление данных конечному пользователю;
* выделенный сервер приложений - здесь содержится бизнес-логика приложения;
* сервер БД - предоставляет запрашиваемые данные.

**Сервер приложений (application server)** – сервисная программа, которая обеспечивает доступ клиентов к прикладным программам, выполняющимся на сервере.

Большинство серверов приложений имеют в своем составе веб-сервер. Это означает, что сервер приложений может делать все, на что способен веб-сервер. Кроме того, сервер приложений имеет компоненты и функции для поддержки сервисов уровня приложения, таких как пул соединений, поддержка транзакций и так далее.

{% hint style="info" %}
Информация о сервере приложений - <http://bit.ly/2qt2Q4t>. \
Отличия веб-сервера и сервера приложений - <http://bit.ly/2qlUaNe>\
Подробнее про сервлеты и контейнеры сервлетов - <http://bit.ly/2Q9GAaP>
{% endhint %}

### Фреймворк Spring

**Spring** – свободно-распространяемый легковесный фреймворк, призванный упростить разработку корпоративных и веб-приложений (можно использовать и для любых других типов приложений) на языке Java (является альтернативной стеку Jakarta EE).

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-LtaZ_4H5iU8wIgV5pDu%2F-Lta_quWkLgtNog-OVL5%2Fimage.png?alt=media\&token=b0cd5d93-2cd3-4637-a149-86dffe554d68)

В данный момент Spring представляет собой целый набор модулей, которые можно использовать выборочно для тех или иных проектов.

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-LtaZ_4H5iU8wIgV5pDu%2F-Lta_uvl51mwhOi_-HRH%2Fimage.png?alt=media\&token=05b995cf-7cd2-40b6-ba10-1936fcc9c2bd)

Дадим краткую характеристику **некоторым модулям** **Spring**:

* **Spring** **Core** – ядро платформы, предоставляет базовые средства для создания приложений — управление компонентами (бинами, beans), внедрение зависимостей, MVC фреймворк, транзакции, базовый доступ к БД. В основном это низкоуровневые компоненты и абстракции. По сути, неявно используется всеми другими компонентами;
* **Spring** **MVC** – обеспечивает архитектуру паттерна Model-View-Controller при помощи слабо связанных готовых компонентов для разработки веб-приложений;
* **Spring Data** – обеспечивает доступ к данным: реляционные и нереляционные БД, KV хранилища и т.п.;
* **Spring Cloud** – используется для микросервисной архитектуры;
* **Spring Security** – авторизация и аутентификация, доступ к данным, методам и т.п. OAuth, LDAP, и различные провайдеры.

### **Spring** **Boot**

Проект **Spring** **Boot** – решение, которое позволяет вам легко создавать полноценные приложения Spring, про которые можно сказать «просто запусти».

Spring Boot позволяет быстро создать и сконфигурировать (т.е. настроить зависимости между компонентами) приложение, упаковать его в исполняемый самодостаточный артефакт. Это то связующее звено, которое объединяет вместе набор компонентов в готовое приложение.

**Особенности** **Spring Boot:**

* создание полноценных Spring-приложений;
* встроенный сервлет-контейнер (Tomcat или Jetty);
* обеспечивает начальные pom-файлы для упрощения конфигурации Maven;
* используется автоконфигурация, где это возможно;
* используется принцип «**convention over configuration**». Для большинства конфигураций не нужно ничего настраивать.

Изучение фреймворка Spring лучше всего начать с установки требуемого программного обеспечения и разработки тестового приложения с помощью Spring Boot.

### Веб-сервисы

Всемирная паутина является готовой платформой для создания и использования распределенных систем на основе веб-служб. Веб-сервер выступает в качестве сервера приложений, к которым обращаются не конечные пользователи, а сторонние приложения. Это позволяет многократно использовать функциональные элементы, устранить дублирование кода, упростить решение задач интеграции приложений.

**Веб-служба или веб-сервис (web-service)** – сетевая технология, обеспечивающая межпрограммное взаимодействие на основе веб-стандартов. W3C определяет веб-службу как «**программную систему, разработанную для поддержки интероперабельного межкомпьютерного (machine-to-machine) взаимодействия через сеть**».

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltaasvk4qPeBIKCrQxI%2F-Ltad8NIbWo1u3xB4y8N%2Fimage.png?alt=media\&token=9a8a3cb7-691c-4407-b291-7dfb78ce1c3f)

К моменту появления веб-служб уже существовали технологии, позволяющие приложениям взаимодействовать на расстоянии, где одна программа могла вызвать какой-нибудь другой метод в другой программе, которая при этом могла быть запущена на компьютере, расположенном в другом городе или даже стране. Это сокращенно называется **RPC** **(Remote** **Procedure** **Calling** **– удаленный вызов процедур)**. В качестве примеров можно привести технологии CORBA, а для Java – RMI (Remote Method Invoking – удаленный вызов методов).

Идея веб-службы заключалась в создании такого RPC, который будет упаковываться в HTTP пакеты. Такой подход стал очень популярным, т.к. HTTP был хорошо известен, прост, понятен и обеспечивал лучшее «прохождение» через различные firewall\`ы. Именно с появлением веб-сервисов развилась идея **SOA** **– сервис-ориентированной архитектуры веб-приложений** (Service Oriented Architecture).

### Протокол HTTP

Протокол HTTP лежит в основе обмена данными в Интернете. HTTP является протоколом клиент-серверного взаимодействия, что означает инициирование запросов к серверу самим получателем (браузером или другим клиентским приложением).

Клиенты и серверы взаимодействуют, обмениваясь одиночными сообщениями (а не потоком данных). Сообщения, отправленные клиентом называются **запросами**, а сообщения, отправленные сервером, называются **ответами**.

HTTP - это клиент-серверный протокол, то есть запросы отправляются какой-то одной стороной - участником обмена (user-agent). Чаще всего в качестве участника выступает веб-браузер, но им может быть кто угодно.

Каждый запрос (request) отправляется серверу, который обрабатывает его и возвращает ответ (response).

Участник обмена (user agent) - это любой инструмент или устройство, действующее от лица пользователя.

На другой стороне коммуникационного канала расположен сервер, который обслуживает (serve) пользователя, предоставляя ему документы по запросу. С точки зрения конечного пользователя, сервер всегда является некой одной виртуальной машиной, полностью или частично генерирующий документ, хотя фактически он может быть группой серверов, между которыми балансируется нагрузка, то есть перераспределяются запросы различных пользователей, либо сложным программным обеспечением, опрашивающим другие компьютеры.

#### HTTP-запросы (HTTP-request)

Пример HTTP запроса

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltut0w3f3_7UxGZX6_8%2F-LtutbFmHConTmja6R0u%2Fimage.png?alt=media\&token=8a0af8fa-33ac-4597-90c0-82c45350f405)

Запросы содержат следующие элементы:

* HTTP-метод, обычно глагол подобно **GET**, **POST** или существительное, как OPTIONS или HEAD, определяющее операцию, которую клиент хочет выполнить. Обычно, клиент хочет получить ресурс (используя GET) или передать значения HTML-формы (используя POST), хотя другие операции могут быть необходимы в других случаях;
* путь к ресурсу;
* заголовки (опционально), предоставляющие дополнительную информацию для сервера;
* для некоторых методов, таких как POST, тело метода, которое содержит отправленный ресурс.

#### HTTP-ответы

Пример HTTP-ответа

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltut0w3f3_7UxGZX6_8%2F-Ltuu4-900yCPN0De49h%2FHTTP_Response.png?alt=media\&token=436ac41f-6e6d-4dba-9fbf-cfc86a38c983)

Ответы содержат следующие элементы:

* версию HTTP-протокола;
* HTTP код состояния, сообщающий об успешности запроса или причине неудачи;
* сообщение состояния - краткое описание кода состояния;
* опционально: тело, содержащее пересылаемый ресурс.

**Код состояния** - это трехзначное число, которое отдает сервер на запрос клиента и благодаря которому корректируется дальнейшая обработка запрашиваемого документа. За числом всегда идет краткое пояснение кода на английском языке, отделенное пробелом - первичная инструкция клиенту.

**Классы состояния** - группа кодов, объединенных определенными признаками. На класс состояния указывает первая цифра в коде.

Выделяют пять классов:

* **1ХХ** - информационные кода. Они отвечают за процесс передачи данных. Это временные коды, они информируют о том, что запрос принят и обработка будет продолжаться;
* **2ХХ** - успешная обработка. Запрос был получен и успешно обработан сервером;
* **3ХХ** - перенаправление (редирект). Эти ответы сервера гласят, что нужно предпринять дальнейшие действия для выполнения запроса. Например, сделать запрос по другому адресу;
* **4ХХ** - ошибка клиента. Это значит, что запрос не может быть выполнен на стороне клиента;
* **5ХХ** - ошибка сервера. Эти коды возникают из-за ошибок на стороне сервера. В данном случае клиент сделал все правильно, но сервер не может выполнить запрос. Для кодов этого класса сервер обязательно показывает сообщение, что не может обработать запрос и по какой причине.

### **Протоколы веб-сервисов**

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

* SOAP (Simple Object Access Protocol) – тройка стандартов SOAP/WSDL/UDDI. Сообщения упаковываются в виде структуры, которая называется конверт (envelope), которая включает идентификатор сообщения, заголовок и тело сообщения;
* REST (Representational State Transfer) – архитектурны стиль, который использует концепцию ресурсов и определяет операции через методы HTTP-протокола;
* XML-RPC (XML Remote Procedure Call) – вызов удаленных процедур, использующий XML для кодирования своих сообщений и HTTP в качестве транспортного механизма.

### **Протокол** **REST**

Передача состояния представления (**Re**presentational **S**tate **T**ransfer (REST)) является архитектурным стилем, в котором веб-службы рассматриваются, как ресурсы и могут быть идентифицированы Унифицированными идентификаторами ресурсов (**U**niform **R**esource **I**dentifiers (URI)).

Веб-службы, разработанные в стиле REST и с учетом ограничений REST, известны как **RESTful** **веб-службы**.

Каждая единица информации в REST называется ресурсом и имеет однозначный URI, который является ее, своего рода, первичным ключом. То есть, например, третья книга с книжной полки будет иметь URI **/book/3**, а 35ая страница в этой книге – **/book/3/page/35/**. Отсюда и получается строго заданный формат. Причем совершенно не имеет значения, в каком формате находятся данные по адресу **/book/3/page/35/** – это может быть и HTML, и отсканированная копия книги в виде jpeg-файла и документ Microsoft Word.

Над ресурсами выполняется ряд простых четко определенных операций. В качестве протокола передачи данных используется stateless-протокол, обычно HTTP.

При использовании протокола HTTP действия над данными выполняются с помощью HTTP-методов: **GET** (получить), **PUT** (добавить, заменить), **POST** (добавить, изменить, удалить), **DELETE** (удалить). Таким образом, действия CRUD (Create-Read-Update-Delete) могут выполняться как со всеми 4-мя методами, так и только с помощью GET и POST. Примеры запросов:

* GET /book/ – получить список всех книг;
* GET /book/3 – получить книгу номер 3;
* PUT /book/ – добавить книгу (данные в теле запроса);
* POST /book/3 – изменить книгу (данные в теле запроса);
* DELETE /book/3 – удалить книгу.

Как правило, необязательно поддерживать все методы, но, как правило, веб-служба должна поддерживать:

* GET – используется для получения существующих ресурсов;
* POST – используется для создания/обновления нового ресурса;
* PUT – используется для обновления/замены ресурса;
* DELETE – используется для удаления ресурса.

Кроме этого, служба может поддерживать такие методы как PATCH (обновление части ресурса), HEAD (возвращение заголовка ресурса, т.е. метаданных) и т.д.

## Практическая часть

### Установка программного обеспечения

Для выполнения домашнего задания нам понадобится следующее программное обеспечение:

* JDK - <http://www.oracle.com/technetwork/java/javase/downloads/index.html>;
* приложение Postman - <https://www.getpostman.com/downloads/>;
* среда разработки, которая поддерживает Spring (например, IntelliJ IDEA Ultimate Edition или дистрибутив Eclipse под названием Spring Tool Suite –  <https://spring.io/tools>) либо любая другая IDE с поддержкой Java и Maven.

### Создание Spring Boot проекта

Существует несколько способов создать Spring Boot проект. Из наиболее простых способов можно выделить:

* генерация готового проекта на сайте <https://start.spring.io/> (проект **Spring Initializr**);
* создание проекта средствами IDE.

Создадим проект с помощью мастера Intellij IDEA. Создадим новый Spring Boot проект (выберите пункт Spring Initializr). Необходимо указать JDK, метаданные проекта, а также выбрать из списка модулей нужные нам модули Spring

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltut0w3f3_7UxGZX6_8%2F-Ltuv4_Cp3iDk0fX5tjA%2Fimage.png?alt=media\&token=4f7fa4e9-ad7e-439c-8f8a-6a157ae2269c)

Для выполнения задания нам необходимо выбрать web-модуль. Панель выбранных компонентов будет иметь следующий вид:

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltut0w3f3_7UxGZX6_8%2F-LtuvjzsUc8TFdmOFjF8%2Fimage.png?alt=media\&token=f0aca705-7e4d-490b-bd61-b3cf92bc9948)

После окончания работы мастера создания проектов, мы получим стартовый проект Spring Boot. Рассмотрим структуру проекта и обозначим ключевые файлы:

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltut0w3f3_7UxGZX6_8%2F-Ltuw67XMok_w5URCw0W%2Fimage.png?alt=media\&token=721bfa1c-4c10-4737-8669-951db2cda65b)

* **HotelApplication.java** - стартовый класс Spring Boot приложения;
* **application.properties** - файл с настройками приложения. В нем можно переопределить настройки по умолчанию;
* **pom.xml** - POM-файл проекта. Используется сборщиком Maven.

**POM-файл** (Project Object Model) – это XML-файл, который содержит информацию о деталях проекта, и конфигурации для создания проекта на Maven. Он всегда находится в базовом каталоге проекта. Во время выполнения задач, Maven ищет pom-файл в базовой директории проекта. Он читает его и получает необходимую информацию, после чего выполняет задачи.

Корневым элементом является элемент **\<project>**. Внутри тега project содержится основная и обязательная информация о проекте.

**Зависимости (dependency)** – это те библиотеки, которые непосредственно используются в проекте для компиляции кода или его тестирования.

Мы создаем RESTful веб-службу с помощью Spring Boot, поэтому нам нужно «подтянуть» для нашего проекта различные Spring-модули (библиотеки с классами, jar-файлы).

В обычных проектах нам бы было необходимо добавлять каждую зависимость вручную, но Spring Boot позаботился о нас и предоставил нам своего рода «мета-зависимости». Смысл их в том, что Spring Boot понимает, что если вы создаете web-приложение то вам нужен примерно одинаковый набор jar-файлов, поэтому чтобы не писать каждый jar-файл отдельно, мы указываем одну зависимость, а она уже «подтянет» за нас другие отдельные зависимости для создания веб-приложения.

### Запуск приложения

Теперь давайте сразу запустим приложение. Убедимся, что приложение запущено успешно

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltut0w3f3_7UxGZX6_8%2F-LtuyLsgdeSF_fZgF4sp%2Fimage.png?alt=media\&token=fc440ab3-9326-4ca8-867a-1305aecaff02)

перейдем в браузер и попробуем зайти на сайт.

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltut0w3f3_7UxGZX6_8%2F-LtuykItBpm7_SPdrYfV%2Fimage.png?alt=media\&token=3b3859a2-1267-4344-8842-8741fdd2c7e6)

Как видите, Spring Boot приложение успешно запущено. Так как Spring Boot берет на себя большую часть рутинной работы по созданию и запуску приложения, давайте разберемся, что же происходит, когда мы запускаем приложение:

1. Устанавливается конфигурация приложения по умолчанию;
2. Запускается **контекст приложения** **Spring** **(Spring** **application** **context)** – это контейнер для кода, который работает на сервере (службы, контроллеры и т.д.). Все приложения Spring имеют этот контекст, который запускается при запуске приложения. Spring Boot создает этот контекст при запуске приложения;
3. Выполняется сканирование пути к классам (class path scan). Чтобы добавить код в Spring Boot, необходимо создать свои классы и аннотировать их определенным образом. Например, если вы хотите добавить контроллер, вы создаете класс и аннотируете его с помощью аннотации @Controller и так далее. То есть, вы как бы помечаете ваши классы, что это контроллер, это сервис, это еще что-то. Spring сканирует эти классы и, в зависимости от нашего маркера, он работает с этими классами по-разному. То есть Spring сканирует ваш код и ищет классы с этими аннотациями (помимо маркеров, обычно в аннотациях содержатся другие метаданные, которые дают уточняющую информацию для Spring);
4. Запускается Tomcat-сервер. Мы как раз зашли на сервер через URL и получили страницу с ошибкой, так как на сервере не был предусмотрен обработчик запроса с таким URL. Мы не скачивали Tomcat и не устанавливали его – все за нас сделал Spring Boot.

### Структура enterprise-приложения Spring

Простое приложение Spring имеет трехслойную структуру:

* **Web** **layer** – верхний слой приложения. Он отвечает за обработку ввода пользователя и возврат корректного ответа. Также веб-слой отвечает за обработку исключений, которые могут выбрасываться в других слоях приложения. Так как веб-слой является точкой входа в приложение, он также отвечает за аутентификацию и является первой линией защиты приложения;
* **Service** **layer** – слой сервисов, находится ниже веб-слоя. Этот слой содержит сервисы приложения и инфраструктуры. Сервисы приложения предоставляют публичный API сервисного слоя. Они также отвечают за транзакции и авторизацию. Инфраструктурные сервисы содержат код для взаимодействия с внешними ресурсами, такими как файловая система, базы данных, почтовые сервера и так далее. Часто эти сервисы используются несколькими сервисами приложения;
* **Repository** **layer** – самый нижний слой приложения. Он отвечает за взаимодействие с используемыми хранилищами данных.

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Ltut0w3f3_7UxGZX6_8%2F-LtuzU3rHAnsT1M8m9pd%2Fimage.png?alt=media\&token=9cbf73fe-db99-44cc-9063-e1ac298a2164)

## Разработка Web Layer

Для обработки запросов и возврата данных необходимо предусмотреть соответствующие контроллеры REST-запросов, которые и будут составлять наш веб-слой.

**Контроллер** – это java-класс, методы которого призваны обрабатывать HTTP-запросы. Отличие обычного контроллера от REST-контроллера заключается в том, что в REST-контроллере каждый метод класса возвращает данные вместо представления. Рассмотрим пример простого REST-контроллера. Создадим в проекте пакет **controllers**, внутри которого создадим класс **HelloController**.

{% code title="HelloController.java" %}

```java
@RestController
public class HelloController {}
```

{% endcode %}

Обратите внимание, что мы пометили класс аннотацией **@RestController**. Таким образом, мы даем знать Spring, что это не просто класс, а контроллер REST-запросов. В классе создадим метод, который будет возвращать строку.

{% code title="HelloController.java" %}

```java
@RestController
public class HelloController {
    
    public String hello() {
        return "hello";
    }
}
```

{% endcode %}

Говорят, что методы контроллера «отображаются» на HTTP-запросы. Это значит, что при поступлении определенного HTTP-запроса (с определенным URL и HTTP-методом), будет вызван определенный метод контроллера, который вернет некоторые данные. Этим данные будут упакованы в HTTP-ответ и высланы обратно клиенту.

Нам необходимо сделать так, чтобы наш созданный метод был вызван, когда на сервер поступит HTTP-запрос с определенным URL, например **<http://localhost:8080/hello>**. Для этого необходимо пометить метод аннотацией **@GetMapping** c параметром (“/hello”) – часть URL, на который будет отображаться данный метод.

{% code title="HelloController.java" %}

```java
@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }
}
```

{% endcode %}

{% hint style="info" %}
Для каждого из четырех основных HTTP-метода предусмотрена своя аннотация (**@GetMapping**, **@PostMapping**, **@PutMapping**, **@DeleteMapping**). Метод, помеченный определенной аннотацией, обрабатывает запросы только с определенным HTTP-методом.
{% endhint %}

Запустим сервер, заходим на <http://localhost:8080/hello> и видим строку с ответом.

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-LtvCjEUEBM6kxK2ktyl%2F-LtvFbkFaucRO0wkW-pQ%2Fimage.png?alt=media\&token=ca108cd4-617b-47f7-b2bf-e1e14cfe9ad7)

Что произошло? Строка «hello» была помещена в тело HTTP-ответа, браузер получил text/plain с содержимым «hello» и просто вывел его на экран.

### Передача параметров в REST-запросах

Очень часто клиенту необходимо вместе с запросом передать некоторые параметры запроса, которые уточняют и конкретизируют запрос.

Параметры запроса можно передать несколькими способами. Рассмотрим следующие способы:

* указание параметра в URL-пути (localhost:8080/rooms/256);
* указание параметра в строке запроса, которая идет после URL-пути и отделяется символом ? (localhost:8080/rooms?id=256\&param2=value2);
* передача параметров в теле запроса (часто используется для передачи заполненной пользователем формы или передачи данных в формате JSON).

Рассмотрим, каким образом можно получить и обработать параметры запроса, переданные тем или иным способом.

#### Указание параметра в URL-пути

При создании endpoint, в аннотации необходимо указать вариативную часть и назначить ей идентификатор

```java
@GetMapping("/room/{id}")
public void getRoomById() {
    
}
```

Далее необходимо предусмотреть входной аргумент метода, куда Spring запишет значение вариативной части и указать аннотацию @PathVariable для этой переменной. Также необходимо указать идентификатор, который вы указали в аннотации @GetMapping.

```java
@GetMapping("/room/{id}")
public void getRoomById(@PathVariable(value = "id") int roomId) {
    // ...
}
```

В рамках одного запроса может быть несколько вариативных частей, которые можно считать и обработать

```java
@GetMapping("/room/{id1}/{id2}")
public void getRoomById(@PathVariable(value = "id1") int blockId, @PathVariable(value = "id2") int roomId) {
    // ...
}
```

#### Указание параметра в строке запроса

В этом случае, для каждого параметра запроса создается входной аргумент, указывается аннотация @RequestParam, а также указывается имя параметра.

```java
// запрос: http://localhost:8080/room?room_id=250&block_id=10
@GetMapping("/room")
public void getRoomById(@RequestParam(value = "room_id") int roomId, @RequestParam(value = "block_id") int blockId) {
    // ...
}
```

#### Передача параметров в теле запроса

Если в качестве клиента выступает браузер пользователя, данные от клиента на сервер передаются в виде полей формы, которые заполняет пользователь браузера. В этом случае параметры передаются в теле запроса с помощью метода POST.

Форма может иметь следующие MIME-типы:

* `application/x-www-form-urlencoded`: значения кодируются в кортежах с ключом, разделенных символом `'&'`, с `'='` между ключом и значением. Не буквенно-цифровые символы - [percent encoded](https://developer.mozilla.org/ru/docs/Glossary/percent_encoded): это причина, по которой этот тип не подходит для использования с двоичными данными (вместо этого используйте `multipart/form-data`);
* `multipart/form-data`: каждое значение посылается как блок данных ("body part"), с заданными пользовательским клиентом разделителем ("boundary"), разделяющим каждую часть. Эти ключи даются в заголовки `Content-Disposition` каждой части `text/plain`.

Для обработки данных формы необходимо создать входной аргумент для каждого параметра, для каждого входного аргумента указать аннотацию @RequestParam, а также имя параметра.

```java
@PostMapping("/book")
public void bookRoom(@RequestParam(value = "room_id") int room_id,
                     @RequestParam(value = "firstname") String firstname,
                     @RequestParam(value = "lastname") String lastname,
                     @RequestParam(value = "days") int days) {
    // ...
}
```

{% hint style="info" %}
Существует несколько более простых способов получения данных формы, но в данном курсе они не рассматриваются. Вышеуказанный способ является самым простым и понятным на данном этапе изучения курса.
{% endhint %}

### Передача данных между клиентом и сервером.

Так как язык Java является ОО языком, нам было бы удобно работать с входящими и исходящими данными в объектном виде - было бы здорово, если бы REST-контроллер возвращал бы данные в виде объекта некоторого класса, а не в виде набора полей со значениями. Также было бы здорово, чтобы мы могли просто возвращать клиенту объект или коллекцию объектов некоторых классов без необходимости формировать Map из полей и значений.

Для реализации этого функционала, в Spring используется механизм сериализации и десериализации.

**Сериализация** - это преобразование объекта в последовательность байтов, так что объект можно легко сохранить в постоянное хранилище или передать по каналу связи. Затем поток байтов можно **десериализовать** - преобразовать в реплику исходного объекта.

Язык Java предоставляет стандартный механизм Java Serialization API для создания сериализуемых объектов, однако, он нам не подходит, так как ограничивает возможности для использования различных языков и технологий на стороне клиента и сервера.

Мы можем использовать сторонние библиотеки для сериализации объекта с помощью формата XML или JSON.

Использование формата JSON (<http://bit.ly/32ZelBq>) является более предпочтительным. Для сериализации и десериализации в Spring по-умолчанию используется библиотека Jackson.

{% hint style="info" %}
Библиотека Jackson позволяет гибко настроить процесс сериализаци и десериализации, однако, в рамках данного курса мы будем использовать стандартные механизмы сериализации и десериализации, чтобы уделять этому процессу как можно меньше внимания.
{% endhint %}

Рассмотрим ситуацию, когда нам необходимо вернуть клиенту данные в объектном виде. Создадим класс с несколькими полями, создадим объект и вернем его в качестве результата GET-запроса.

{% tabs %}
{% tab title="REST controller" %}

```java
    @GetMapping("/roominfo")
    public Room getRoomInfoById(@RequestParam(value = "room_id") int roomId) {
        return new Room(roomId, "Отличная комната!", 2, 200);
    }
```

{% endtab %}

{% tab title="Room" %}
{% code title="Room.java" %}

```java
public class Room {
    private Integer id;
    private String roomInfo;
    private Integer roomCapacity;
    private double price;

    public Room(Integer id, String roomInfo, Integer roomCapacity, double price) {
        this.id = id;
        this.roomInfo = roomInfo;
        this.roomCapacity = roomCapacity;
        this.price = price;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getRoomInfo() {
        return roomInfo;
    }

    public void setRoomInfo(String roomInfo) {
        this.roomInfo = roomInfo;
    }

    public Integer getRoomCapacity() {
        return roomCapacity;
    }

    public void setRoomCapacity(Integer roomCapacity) {
        this.roomCapacity = roomCapacity;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

{% hint style="warning" %}
Обратите внимание, что в код класса Room не зря были включены геттеры и сеттеры. Их наличие обязательно для сериализации и десериализации!
{% endhint %}

Используем Postman для эмуляции клиента, сделаем GET-запрос и получим следующий результат

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Lu4Y79NotsU2bNey67W%2F-Lu4re3HHqybK-nafmCL%2Fimage.png?alt=media\&token=f4738c89-4387-45d5-9aab-4e125bda0504)

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

Теперь рассмотрим обратную ситуацию. Клиент делает POST-запрос и передает в теле запроса данные о новом студенте.

![](https://1377473627-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LnhxGhlv6e_zwoZYywB%2F-Lu60hRFe9hRZmKQKfkD%2F-Lu61zOI4ucGc2xZ4puh%2Fimage.png?alt=media\&token=e6275a73-9e3f-4908-8a50-1e9448134451)

На стороне сервера создаем класс **Student** с соответствующими полями.

{% code title="Student.java" %}

```java
public class Student {

    private String firstname;
    private String lastname;
    private String phone;
    private int age;

    public Student(String firstname, String lastname, String phone, int age) {
        this.firstname = firstname;
        this.lastname = lastname;
        this.phone = phone;
        this.age = age;
    }

    public String getFirstname() {
        return firstname;
    }

    public void setFirstname(String firstname) {
        this.firstname = firstname;
    }

    public String getLastname() {
        return lastname;
    }

    public void setLastname(String lastname) {
        this.lastname = lastname;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "firstname='" + firstname + '\'' +
                ", lastname='" + lastname + '\'' +
                ", phone='" + phone + '\'' +
                ", age=" + age +
                '}';
    }
}
```

{% endcode %}

Создаем конечную точку для обработки запроса. Обратите внимание, что мы используем аннотацию **@RequestBody**.

```java
@PostMapping("/student")
public void addStudent(@RequestBody Student student) {
    System.out.println(student);
}
```

### Конфигурация HTTP-response с помощью класса ResponseEntity

Далеко не всегда ответ сервера состоит в возврате какого-то значения или какого-то объекта. Очень часто необходимо вернуть ответ с определенным HTTP-кодом и сообщением об ошибке, указать определенный заголовок и так далее.

В этом случае необходимо использовать класс ResponeEntity. Класс **ResponseEntity** является оберткой для ответа и дополнительно для HTTP заголовков и кода статуса. Он является обобщенным, что позволяет использовать любой тип в качестве тела ответа.

```java
@GetMapping("/student")
public ResponseEntity<?> getStudentById(@RequestParam(value = "id") int studentId) {
    if (studentId < 1) {
        return ResponseEntity.badRequest().body("Invalid id");
    } else {
        return ResponseEntity.ok(new Student("Ivan", "Ivanov", "223322", 20));
    }
}
```

Подробную информацию по поводу ResponseEntity читайте здесь - <https://www.baeldung.com/spring-response-entity>.
