Сервер - компьютер или программа, которая управляет ресурсами (информация, файлы, база данных) называется сервером этого ресурса или просто сервером.
Архитектура "клиент-сервер" определяет общие принципы организации взаимодействия, где имеются серверы (узлы-поставщики некоторых специфичных функций и сервисов) и клиенты, (потребители этих сервисов).
Между клиентами и серверами должны быть установлены правила взаимодействия, которые называются протоколом взаимодействия или протоколом обмена. Каждая часть взаимодействует друг с другом, обмениваясь сообщениями в заранее согласованном формате.
Более подробно про клиент-серверное взаимодействие читайте здесь - http://bit.ly/2qmKbHk
В рамках данного курса рассматривается так называемая "трехзвенная архитектура"
Компоненты трехзвенной архитектуры:
клиент - этот компонент отвечает за представление данных конечному пользователю;
выделенный сервер приложений - здесь содержится бизнес-логика приложения;
сервер БД - предоставляет запрашиваемые данные.
Сервер приложений (application server) – сервисная программа, которая обеспечивает доступ клиентов к прикладным программам, выполняющимся на сервере.
Большинство серверов приложений имеют в своем составе веб-сервер. Это означает, что сервер приложений может делать все, на что способен веб-сервер. Кроме того, сервер приложений имеет компоненты и функции для поддержки сервисов уровня приложения, таких как пул соединений, поддержка транзакций и так далее.
Информация о сервере приложений - http://bit.ly/2qt2Q4t. Отличия веб-сервера и сервера приложений - http://bit.ly/2qlUaNe Подробнее про сервлеты и контейнеры сервлетов - http://bit.ly/2Q9GAaP
Spring – свободно-распространяемый легковесный фреймворк, призванный упростить разработку корпоративных и веб-приложений (можно использовать и для любых других типов приложений) на языке Java (является альтернативной стеку Jakarta EE).
В данный момент Spring представляет собой целый набор модулей, которые можно использовать выборочно для тех или иных проектов.
Дадим краткую характеристику некоторым модулям Spring:
Spring Core – ядро платформы, предоставляет базовые средства для создания приложений — управление компонентами (бинами, beans), внедрение зависимостей, MVC фреймворк, транзакции, базовый доступ к БД. В основном это низкоуровневые компоненты и абстракции. По сути, неявно используется всеми другими компонентами;
Spring MVC – обеспечивает архитектуру паттерна Model-View-Controller при помощи слабо связанных готовых компонентов для разработки веб-приложений;
Spring Data – обеспечивает доступ к данным: реляционные и нереляционные БД, KV хранилища и т.п.;
Spring Cloud – используется для микросервисной архитектуры;
Spring Security – авторизация и аутентификация, доступ к данным, методам и т.п. OAuth, LDAP, и различные провайдеры.
Проект Spring Boot – решение, которое позволяет вам легко создавать полноценные приложения Spring, про которые можно сказать «просто запусти».
Spring Boot позволяет быстро создать и сконфигурировать (т.е. настроить зависимости между компонентами) приложение, упаковать его в исполняемый самодостаточный артефакт. Это то связующее звено, которое объединяет вместе набор компонентов в готовое приложение.
Особенности Spring Boot:
создание полноценных Spring-приложений;
встроенный сервлет-контейнер (Tomcat или Jetty);
обеспечивает начальные pom-файлы для упрощения конфигурации Maven;
используется автоконфигурация, где это возможно;
используется принцип «convention over configuration». Для большинства конфигураций не нужно ничего настраивать.
Изучение фреймворка Spring лучше всего начать с установки требуемого программного обеспечения и разработки тестового приложения с помощью Spring Boot.
Всемирная паутина является готовой платформой для создания и использования распределенных систем на основе веб-служб. Веб-сервер выступает в качестве сервера приложений, к которым обращаются не конечные пользователи, а сторонние приложения. Это позволяет многократно использовать функциональные элементы, устранить дублирование кода, упростить решение задач интеграции приложений.
Веб-служба или веб-сервис (web-service) – сетевая технология, обеспечивающая межпрограммное взаимодействие на основе веб-стандартов. W3C определяет веб-службу как «программную систему, разработанную для поддержки интероперабельного межкомпьютерного (machine-to-machine) взаимодействия через сеть».
К моменту появления веб-служб уже существовали технологии, позволяющие приложениям взаимодействовать на расстоянии, где одна программа могла вызвать какой-нибудь другой метод в другой программе, которая при этом могла быть запущена на компьютере, расположенном в другом городе или даже стране. Это сокращенно называется RPC (Remote Procedure Calling – удаленный вызов процедур). В качестве примеров можно привести технологии CORBA, а для Java – RMI (Remote Method Invoking – удаленный вызов методов).
Идея веб-службы заключалась в создании такого RPC, который будет упаковываться в HTTP пакеты. Такой подход стал очень популярным, т.к. HTTP был хорошо известен, прост, понятен и обеспечивал лучшее «прохождение» через различные firewall`ы. Именно с появлением веб-сервисов развилась идея SOA – сервис-ориентированной архитектуры веб-приложений (Service Oriented Architecture).
Протокол HTTP лежит в основе обмена данными в Интернете. HTTP является протоколом клиент-серверного взаимодействия, что означает инициирование запросов к серверу самим получателем (браузером или другим клиентским приложением).
Клиенты и серверы взаимодействуют, обмениваясь одиночными сообщениями (а не потоком данных). Сообщения, отправленные клиентом называются запросами, а сообщения, отправленные сервером, называются ответами.
HTTP - это клиент-серверный протокол, то есть запросы отправляются какой-то одной стороной - участником обмена (user-agent). Чаще всего в качестве участника выступает веб-браузер, но им может быть кто угодно.
Каждый запрос (request) отправляется серверу, который обрабатывает его и возвращает ответ (response).
Участник обмена (user agent) - это любой инструмент или устройство, действующее от лица пользователя.
На другой стороне коммуникационного канала расположен сервер, который обслуживает (serve) пользователя, предоставляя ему документы по запросу. С точки зрения конечного пользователя, сервер всегда является некой одной виртуальной машиной, полностью или частично генерирующий документ, хотя фактически он может быть группой серверов, между которыми балансируется нагрузка, то есть перераспределяются запросы различных пользователей, либо сложным программным обеспечением, опрашивающим другие компьютеры.
Пример HTTP запроса
Запросы содержат следующие элементы:
HTTP-метод, обычно глагол подобно GET, POST или существительное, как OPTIONS или HEAD, определяющее операцию, которую клиент хочет выполнить. Обычно, клиент хочет получить ресурс (используя GET) или передать значения HTML-формы (используя POST), хотя другие операции могут быть необходимы в других случаях;
путь к ресурсу;
заголовки (опционально), предоставляющие дополнительную информацию для сервера;
для некоторых методов, таких как POST, тело метода, которое содержит отправленный ресурс.
Пример HTTP-ответа
Ответы содержат следующие элементы:
версию 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 в качестве транспортного механизма.
Передача состояния представления (Representational State Transfer (REST)) является архитектурным стилем, в котором веб-службы рассматриваются, как ресурсы и могут быть идентифицированы Унифицированными идентификаторами ресурсов (Uniform Resource Identifiers (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 (возвращение заголовка ресурса, т.е. метаданных) и т.д.
Для выполнения домашнего задания нам понадобится следующее программное обеспечение:
приложение Postman - https://www.getpostman.com/downloads/;
среда разработки, которая поддерживает Spring (например, IntelliJ IDEA Ultimate Edition или дистрибутив Eclipse под названием Spring Tool Suite – https://spring.io/tools) либо любая другая IDE с поддержкой Java и Maven.
Существует несколько способов создать Spring Boot проект. Из наиболее простых способов можно выделить:
генерация готового проекта на сайте https://start.spring.io/ (проект Spring Initializr);
создание проекта средствами IDE.
Создадим проект с помощью мастера Intellij IDEA. Создадим новый Spring Boot проект (выберите пункт Spring Initializr). Необходимо указать JDK, метаданные проекта, а также выбрать из списка модулей нужные нам модули Spring
Для выполнения задания нам необходимо выбрать web-модуль. Панель выбранных компонентов будет иметь следующий вид:
После окончания работы мастера создания проектов, мы получим стартовый проект Spring Boot. Рассмотрим структуру проекта и обозначим ключевые файлы:
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-файл отдельно, мы указываем одну зависимость, а она уже «подтянет» за нас другие отдельные зависимости для создания веб-приложения.
Теперь давайте сразу запустим приложение. Убедимся, что приложение запущено успешно
перейдем в браузер и попробуем зайти на сайт.
Как видите, Spring Boot приложение успешно запущено. Так как Spring Boot берет на себя большую часть рутинной работы по созданию и запуску приложения, давайте разберемся, что же происходит, когда мы запускаем приложение:
Устанавливается конфигурация приложения по умолчанию;
Запускается контекст приложения Spring (Spring application context) – это контейнер для кода, который работает на сервере (службы, контроллеры и т.д.). Все приложения Spring имеют этот контекст, который запускается при запуске приложения. Spring Boot создает этот контекст при запуске приложения;
Выполняется сканирование пути к классам (class path scan). Чтобы добавить код в Spring Boot, необходимо создать свои классы и аннотировать их определенным образом. Например, если вы хотите добавить контроллер, вы создаете класс и аннотируете его с помощью аннотации @Controller и так далее. То есть, вы как бы помечаете ваши классы, что это контроллер, это сервис, это еще что-то. Spring сканирует эти классы и, в зависимости от нашего маркера, он работает с этими классами по-разному. То есть Spring сканирует ваш код и ищет классы с этими аннотациями (помимо маркеров, обычно в аннотациях содержатся другие метаданные, которые дают уточняющую информацию для Spring);
Запускается Tomcat-сервер. Мы как раз зашли на сервер через URL и получили страницу с ошибкой, так как на сервере не был предусмотрен обработчик запроса с таким URL. Мы не скачивали Tomcat и не устанавливали его – все за нас сделал Spring Boot.
Простое приложение Spring имеет трехслойную структуру:
Web layer – верхний слой приложения. Он отвечает за обработку ввода пользователя и возврат корректного ответа. Также веб-слой отвечает за обработку исключений, которые могут выбрасываться в других слоях приложения. Так как веб-слой является точкой входа в приложение, он также отвечает за аутентификацию и является первой линией защиты приложения;
Service layer – слой сервисов, находится ниже веб-слоя. Этот слой содержит сервисы приложения и инфраструктуры. Сервисы приложения предоставляют публичный API сервисного слоя. Они также отвечают за транзакции и авторизацию. Инфраструктурные сервисы содержат код для взаимодействия с внешними ресурсами, такими как файловая система, базы данных, почтовые сервера и так далее. Часто эти сервисы используются несколькими сервисами приложения;
Repository layer – самый нижний слой приложения. Он отвечает за взаимодействие с используемыми хранилищами данных.
Для обработки запросов и возврата данных необходимо предусмотреть соответствующие контроллеры REST-запросов, которые и будут составлять наш веб-слой.
Контроллер – это java-класс, методы которого призваны обрабатывать HTTP-запросы. Отличие обычного контроллера от REST-контроллера заключается в том, что в REST-контроллере каждый метод класса возвращает данные вместо представления. Рассмотрим пример простого REST-контроллера. Создадим в проекте пакет controllers, внутри которого создадим класс HelloController.
Обратите внимание, что мы пометили класс аннотацией @RestController. Таким образом, мы даем знать Spring, что это не просто класс, а контроллер REST-запросов. В классе создадим метод, который будет возвращать строку.
Говорят, что методы контроллера «отображаются» на HTTP-запросы. Это значит, что при поступлении определенного HTTP-запроса (с определенным URL и HTTP-методом), будет вызван определенный метод контроллера, который вернет некоторые данные. Этим данные будут упакованы в HTTP-ответ и высланы обратно клиенту.
Нам необходимо сделать так, чтобы наш созданный метод был вызван, когда на сервер поступит HTTP-запрос с определенным URL, например http://localhost:8080/hello. Для этого необходимо пометить метод аннотацией @GetMapping c параметром (“/hello”) – часть URL, на который будет отображаться данный метод.
Для каждого из четырех основных HTTP-метода предусмотрена своя аннотация (@GetMapping, @PostMapping, @PutMapping, @DeleteMapping). Метод, помеченный определенной аннотацией, обрабатывает запросы только с определенным HTTP-методом.
Запустим сервер, заходим на http://localhost:8080/hello и видим строку с ответом.
Что произошло? Строка «hello» была помещена в тело HTTP-ответа, браузер получил text/plain с содержимым «hello» и просто вывел его на экран.
Очень часто клиенту необходимо вместе с запросом передать некоторые параметры запроса, которые уточняют и конкретизируют запрос.
Параметры запроса можно передать несколькими способами. Рассмотрим следующие способы:
указание параметра в URL-пути (localhost:8080/rooms/256);
указание параметра в строке запроса, которая идет после URL-пути и отделяется символом ? (localhost:8080/rooms?id=256¶m2=value2);
передача параметров в теле запроса (часто используется для передачи заполненной пользователем формы или передачи данных в формате JSON).
Рассмотрим, каким образом можно получить и обработать параметры запроса, переданные тем или иным способом.
При создании endpoint, в аннотации необходимо указать вариативную часть и назначить ей идентификатор
Далее необходимо предусмотреть входной аргумент метода, куда Spring запишет значение вариативной части и указать аннотацию @PathVariable для этой переменной. Также необходимо указать идентификатор, который вы указали в аннотации @GetMapping.
В рамках одного запроса может быть несколько вариативных частей, которые можно считать и обработать
В этом случае, для каждого параметра запроса создается входной аргумент, указывается аннотация @RequestParam, а также указывается имя параметра.
Если в качестве клиента выступает браузер пользователя, данные от клиента на сервер передаются в виде полей формы, которые заполняет пользователь браузера. В этом случае параметры передаются в теле запроса с помощью метода POST.
Форма может иметь следующие MIME-типы:
application/x-www-form-urlencoded
: значения кодируются в кортежах с ключом, разделенных символом '&'
, с '='
между ключом и значением. Не буквенно-цифровые символы - percent encoded: это причина, по которой этот тип не подходит для использования с двоичными данными (вместо этого используйте multipart/form-data
);
multipart/form-data
: каждое значение посылается как блок данных ("body part"), с заданными пользовательским клиентом разделителем ("boundary"), разделяющим каждую часть. Эти ключи даются в заголовки Content-Disposition
каждой части text/plain
.
Для обработки данных формы необходимо создать входной аргумент для каждого параметра, для каждого входного аргумента указать аннотацию @RequestParam, а также имя параметра.
Существует несколько более простых способов получения данных формы, но в данном курсе они не рассматриваются. Вышеуказанный способ является самым простым и понятным на данном этапе изучения курса.
Так как язык Java является ОО языком, нам было бы удобно работать с входящими и исходящими данными в объектном виде - было бы здорово, если бы REST-контроллер возвращал бы данные в виде объекта некоторого класса, а не в виде набора полей со значениями. Также было бы здорово, чтобы мы могли просто возвращать клиенту объект или коллекцию объектов некоторых классов без необходимости формировать Map из полей и значений.
Для реализации этого функционала, в Spring используется механизм сериализации и десериализации.
Сериализация - это преобразование объекта в последовательность байтов, так что объект можно легко сохранить в постоянное хранилище или передать по каналу связи. Затем поток байтов можно десериализовать - преобразовать в реплику исходного объекта.
Язык Java предоставляет стандартный механизм Java Serialization API для создания сериализуемых объектов, однако, он нам не подходит, так как ограничивает возможности для использования различных языков и технологий на стороне клиента и сервера.
Мы можем использовать сторонние библиотеки для сериализации объекта с помощью формата XML или JSON.
Использование формата JSON (http://bit.ly/32ZelBq) является более предпочтительным. Для сериализации и десериализации в Spring по-умолчанию используется библиотека Jackson.
Библиотека Jackson позволяет гибко настроить процесс сериализаци и десериализации, однако, в рамках данного курса мы будем использовать стандартные механизмы сериализации и десериализации, чтобы уделять этому процессу как можно меньше внимания.
Рассмотрим ситуацию, когда нам необходимо вернуть клиенту данные в объектном виде. Создадим класс с несколькими полями, создадим объект и вернем его в качестве результата GET-запроса.
Обратите внимание, что в код класса Room не зря были включены геттеры и сеттеры. Их наличие обязательно для сериализации и десериализации!
Используем Postman для эмуляции клиента, сделаем GET-запрос и получим следующий результат
Как мы видим, поля объекта были сериализованы с помощью формата JSON. Теперь клиент, после получения этих данных, сможет с помощью процесса десериализации получить объект и удобно работать с ним.
Теперь рассмотрим обратную ситуацию. Клиент делает POST-запрос и передает в теле запроса данные о новом студенте.
На стороне сервера создаем класс Student с соответствующими полями.
Создаем конечную точку для обработки запроса. Обратите внимание, что мы используем аннотацию @RequestBody.
Далеко не всегда ответ сервера состоит в возврате какого-то значения или какого-то объекта. Очень часто необходимо вернуть ответ с определенным HTTP-кодом и сообщением об ошибке, указать определенный заголовок и так далее.
В этом случае необходимо использовать класс ResponeEntity. Класс ResponseEntity является оберткой для ответа и дополнительно для HTTP заголовков и кода статуса. Он является обобщенным, что позволяет использовать любой тип в качестве тела ответа.
Подробную информацию по поводу ResponseEntity читайте здесь - https://www.baeldung.com/spring-response-entity.
Spring MVC – веб-фреймворк, призванный упростить разработку веб-приложений. Опираясь на шаблон модель–представление–контроллер (Model-View-Controller, MVC), фреймворк Spring MVC помогает строить веб-приложения, столь же гибкие и слабо связанные, как сам фреймворк Spring.
Схема работы фреймворка Spring MVC
Схема работы фреймворка выглядит следующим образом:
Краткое описание схемы работы Spring MVC звучит следующим образом:
вначале DispatcherServlet (диспетчер сервлетов) получает запрос, далее он смотрит свои настройки, чтобы понять какой контроллер использовать (на рисунке Handler Mapping);
после получения имени контроллера запрос передается на обработку в этот контроллер (на рисунке Controller). В контроллере происходит обработка запроса и обратно посылается ModelAndView (модель — сами данные; view (представление) — как эти данные отображать);
DispatcherServlet на основании полученного ModelAndView, должен определить, какое представление будет выводить данные. Для этого используется арбитр представлений (View Resolver), который на основании полученного логического имени представления возвращает ссылку на файл View;
в представление передаются данные (Model) и обратно, если необходимо, посылается ответ от представления.
Давайте рассмотрим этот процесс более подробно:
Когда запрос покидает браузер, он несет в себе информацию о требовании пользователя. По крайней мере, запрос будет нести в себе запрошенный URL. Но он может также нести дополнительные данные, такие как информация из формы, заполненной пользователем;
Первой остановкой на пути запроса является DispatcherServlet. Как и большинство веб-фреймворков на языке Java, фреймворк Spring MVC пропускает все входящие запросы через единственный сервлет входного контроллера. Входной контроллер (front controller) является типичным шаблоном проектирования веб-приложений, где единственный сервлет берет на себя ответственность за передачу всех запросов остальным компонентам приложения, выполняющим фактическую их обработку. В Spring MVC входным контроллером является DispatcherServlet;
Задача контроллера DispatcherServlet состоит в том, чтобы передать запрос контроллеру Spring MVC. Контроллер – это компонент Spring, обрабатывающий запрос. Но приложение может иметь несколько контроллеров, и входному контроллеру DispatcherServlet требуется помощь, чтобы определить, какому контроллеру передать запрос. Поэтому контроллер DispatcherServlet консультируется c одним или несколькими механизмами отображения (Handler Mapping) и выясняет, какой контроллер будет обрабатывать тот или иной запрос. При принятии решения механизм отображения в первую очередь руководствуется адресом URL в запросе;
Как только будет выбран соответствующий контроллер, DispatcherServlet отправляет запрос в путь к выбранному контроллеру. Достигнув контроллера, запрос отдаст часть своего груза (информацию, отправленную пользователем) и терпеливо будет ждать, пока контроллер обработает эту информацию. (На самом деле хорошо спроектированный контроллер сам почти не занимается обработкой информации, вместо этого он делегирует ответственность за обработку одному или нескольким служебным объектам);
В результате работы контроллера часто появляется некоторая информация, которая должна быть передана назад пользователю и отображена в браузере. Эта информация называется моделью (Model). Но отправки обратно необработанной информации недостаточно, перед отправкой ее следует представить в удобном для пользователя формате, обычно в HTML. Для этого информация должна быть передана в одно из представлений (View), которыми обычно являются JSP-страницы;
Последнее, что должен сделать контроллер, – упаковать вместе модель и имя представления для отображения результатов в браузере. Затем он отсылает запрос вместе с моделью и именем представления обратно входному контроллеру DispatcherServlet;
Чтобы контроллер не оказался тесно связанным с каким-либо конкретным представлением, имя представления, возвращаемое входному контроллеру DispatcherServlet, не определяет JSP-страницу непосредственно. Фактически оно даже не предполагает, что представление вообще является страницей JSP. Оно является лишь логическим именем представления, используемым затем для поиска фактического представления. Чтобы отобразить логическое имя представления в ссылку на конкретную реализацию, входной контроллер DispatcherServlet обратится к арбитру представлений (view resolver);
Теперь, когда контроллер DispatcherServlet определил, какое представление будет отображать результаты, работа запроса подошла к концу. Его конечная остановка – реализация представления (возможно, страница JSP), куда он доставит модель данных. На этом работа запроса заканчивается. На основе модели данных представление создаст отображение страницы, которое будет отправлено обратно клиенту с другим (не таким трудолюбивым) курьером – объектом ответа.
Рассмотренную выше схему работы фреймворка можно также представить следующей диаграммой
Создадим новый Spring Boot проект, выберем следующие модули
В новом проекте обратите внимание на структуру папок. В папке resources\templates будут содержаться html-файлы с использованием шаблонизатора Thymeleaf.
Создадим файл index.html. Обратите внимание, что в теге html необходимо указать пространство имен th для подключения тегов Thymeleaf.
В проекте Spring Boot MVC страница index будет автоматически передана при переходе на URL "/". Запустим проект и зайдем в браузер.
Создадим две html-страницы для нашего проекта.
Создадим класс контроллера, который обрабатывает GET запросы с URL "/" и "/add_student".
В файле index.html добавим ссылку для кнопки "Добавить студента" . Для формирования ссылки используем тег th:href. Для указания пути относительно домена используем комбинацию @{~}.
Проверим работу приложения в браузере
Нажмем на кнопку "Добавить студента"
Для обработки формы необходимо выполнить следующую последовательность действий:
Создать объект, поля которого будут содержать данные формы. В нашем случае создадим класс Student;
Передать пустой объект Student при переходе на страницу формы;
В полях формы настроить соответствие между полями формы и полями объекта класса Student;
После отсылки формы заполненный объект Student передается в теле HTTP-запроса с методом POST, после чего заполненный объект можно обработать, сохранить в базе данных и так далее.
Создадим класс Student
Изменим метод контроллера, который обрабатывает URL "/add_student". Передадим пустой объект студента
Изменим файл add_student.html. В полях формы добавим привязку к полям объекта, в теге формы укажем название объекта (исходя из метода контроллера он должен называться "student", а также укажем URL для отправки данных формы).
Создадим метод контроллера, который принимает POST-запрос с URL "/add_student". Данный метод будет обрабатывать результат заполнения формы.
Изменим класс Student, превратив его в сущность
Создадим интерфейс репозитория
Добавим класс сервиса для работы с DAO
Изменим класс контроллера. После получения результатов заполнения формы, данные будут сохраняться в базе данных.
ля вывода данных на странице index, необходимо выполнить следующие действия:
обратиться к базе данных для получения списка студентов;
передать список студентов в View;
предусмотреть вывод полей каждого студента в нужных ячейках таблицы.
Модифицируем метод контроллера, который отвечает за обработку запроса "\"
Модифицируем страницу index.html. Добавим вывод полей каждого объекта типа Student в таблице
Запустим приложение и посмотрим на результат. Изначально таблица студентов пустая
Добавляем нового студента
После добавления студента, нас перенаправляют на страницу index
Создадим проект с индексной страницей, на которой расположена форма добавления нового студента.
Изначально, форма не имеет средств валидации, то есть мы не можем отследить корректность заполнения формы.
Spring предоставляет несколько инструментов для реализации валидации формы, воспользуемся библиотекой Bean Validation API
Информацию по поводу использования библиотеки можно найти здесь (см. раздел 8 мануала)
Добавим библиотеку в список зависимостей в файле pom.xml
Будем использовать механизм встроенных ограничений. Алгоритм использования встроенных ограничений следующий - с помощью аннотаций необходимо указать над полем класса-сущности требуемые параметры валидации и другие параметры. В нашем случае, сущностью выступает класс Student. Добавим необходимые ограничения для полей сущности.
Как мы видим, все достаточно просто и наглядно.
Далее, нам необходимо модифицировать контроллеры и реализовать следующий функционал:
указать, что объект типа Student должен пройти валидацию;
получить результаты валидации объекта;
если объект не прошел валидацию - не добавлять объект в хранилище, выдать сообщение об ошибке в консоль.
Нам необходимо модифицировать метод контроллера, который обрабатывает данные формы. Указываем аннотацию @Valid, которая говорит о том, что полученный объект необходимо подвергнуть валидации. Далее указываем аргумент типа BindingResult, который хранит информацию о результате валидации. С помощью метода hasErrors() получаем результат валидации объекта.
При попытке отправить пустую форму, получаем сообщение в консоли
Последний шаг - необходимо предоставить пользователю информацию о том, что то или иное поле формы не прошло валидацию.
Самый простой способ проинформировать пользователь - показать сообщение об ошибке около поля, которое не прошло валидацию. Чтобы реализовать данный функционал, перейдем в шаблон index.html.
Рассмотрим поле "Фамилия". Сообщение об ошибке мы разместим снизу поля. Добавим соответствующий элемент <small> в HTML-макет.
Используем тег th:if. Если выражение внутри тега равно true, то элемент <small> будет показан на экране, если false - будет скрыт.
Выражение ${fields.hasErrors('lastName)}
означает, есть ли ошибки валидации для поля lastName
? Если ошибки есть - поле будет показано. Текст ошибки выводим с помощью атрибута th:errors.
Добавляем элементы для вывода ошибок для остальных полей формы. Проверяем результат
Ниже представлен листинг классов и файлов