Controller

  • Рубрика записи:Spring back-end

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

Вперёд к созданию контроллеров!

@Controller и @RestController

Всё-таки, контроллеры бывают разными. Давайте рассмотрим две аннотации и выберем, какую из них использовать в этом конкретном случае.

@Controller используется для создания контроллера, который возвращает представления или шаблоны HTML. Он обычно используется в приложениях, где требуется генерация HTML-страниц на основе данных. То есть возвращаем название страницы, которая будет отрисовываться у клиента в браузере:

Здесь login_page – это название страницы, название шаблона, который нужно вернуть пользователю.

@RestController, с другой стороны, используется для создания RESTful веб-служб, которые возвращают данные в формате JSON или XML. Он объединяет аннотации @Controller и @ResponseBody. Благодаря тому контроллер возвращает данные напрямую без необходимости использовать представления. То есть возвращаем чисто объекты, без каких-либо страниц. Эти объекты потом сможет красиво разложить в HTML-странице или ещё где захочет приложение, запросившее данные:

Так как мы пишем back-end, то нам подходит @RestController. Мы будем возвращать чистые данные. Что с ними делать дальше и как отображать – будет решать тот, кто за этими данными к нам обратится.

Создание класса контроллера

Создадим контроллеры для обработки HTTP-запросов из нашего примера с ранее созданными сущностями User и Post. Что нам для этого понадобится?

Аннотацию контроллера мы уже выбрали. Это @RestController, сырые данные. Дальше нужно привязать сервисы в контроллеры. Почему? Потому что контроллеры эксплуатируют сервисы так же, как сервисы эксплуатируют репозитории:

Схема цепочки действий в Spring-проекте с БД, репозиторием, сервисом и контроллером

Итак, благодаря аннотации @RequiredArgsConstructor из утилиты Lombok я могу просто указать сервис как final-поле контроллера. Тогда Spring автоматически подставит туда объект сервиса.

Также можно использовать аннотацию @Autowired, к примеру, чтобы Spring ровно так же понял, что туда класть:

Ровно такие же манипуляции нужно проделать в классе Controller для сущности Post:

Отлично. Контроллер по умолчанию обрабатывает все запросы (после самого адреса ресурса, на котором висит ваше веб-приложение). Если вы хотите, чтобы он отвечал только на запросы с некоторым префиксом, то есть начинающиеся на определённые символы, тогда следует указать над классом аннотацию @RequestMapping со значением префикса:

Теперь пора приступать к написанию обработчиков HTTP-запросов.

Запросы get, post, put, delete

У нас есть 2 сущности: User и Post. Для них мы создали таблицы users и posts в БД. Также мы создавали для них репозитории (UserRepository, PostRepository) и сервисы (UserService, PostService) в предыдущих статьях. Теперь время написать ответы на запросы в двух классах контроллеров – UserController и PostController.

Есть такая аннотация @RequestMapping. Мы её уже прописывали над классом контроллера. Но теперь мы будем её использовать для методов. @RequestMapping в сущности задаёт путь запросов. И для каждого метода класса контроллера у нас будут разные пути.

@RequestMapping принимает любой тип запроса (например, get, post и другие). Однако есть аннотации-уточнения аннотации @RequestMapping. Они обрабатывают уже только указанный тип запроса. Вот 4 основных, которые используются в подавляющем большинстве случаев:

  • @GetMapping – обработка get-запросов, которые обычно используются для запроса данных с сервера. Он не должен изменять состояние сервера и не должен иметь побочных эффектов.
  • @PostMapping – обработка post-запросов, которые применяются для отправки данных на сервер для обработки. В связи с тем они часто используются при создании новых ресурсов или изменении состояния на сервере.
  • @PutMapping – обработка put-запросов, которые вносят изменения и позволяют обновить данные на сервере.
  • @DeleteMapping – обработка delete-запросов, которые (неожиданно) удаляют ресурсы на сервере.

Пример метода с @GetMapping:

Методы именно с такими 4-мя видами аннотаций мы сейчас и напишем в два контроллера. При этом есть определённые соглашения о путях запросов. Кроме того, есть ещё 2 важные аннотации.

Первая из них – @PathVariable – позволяет обрабатывать переменную из пути запроса прямо в методе, даже указывая её тип данных (в данном случае Long):

Вторая аннотация, @RequestBody, сообщает о теле запроса, которое также можно извлечь из запроса и обработать в методе контроллера:

Итак, вот, какой получился UserController:

Все пути запросов для обращения к пользователям будут начинаться с “/users“. Что ж, здесь есть запросы чтения, создания, обновления и удаления. Всё, как надо! Теперь взглянем на PostController:

Для обращения к записям, в свою очередь, потребуется писать префикс “/posts” в запросах.

Контроллеры готовы. Но для чего весь сыр-бор? Давайте пробовать, как это работает!

Как обратиться к контроллеру из браузера

В случае, если сайт не размещён в интернетах, а просто покоится на самом компьютере, хост имеет название localhost, или же 127.0.0.1 – ip-адрес самого себя. То есть любой компьютер, обращающийся к 127.0.0.1 или localhost, обратится сам к себе.

После названия хоста идёт порт – несколько цифр. Если в настройках вашего Spring-проекта не указано что-то другое, вы будете подключаться по порту 8080. Итого, адрес обращения к контроллеру будет “localhost:8080“. Адрес обращения к UserController составит “localhost:8080/users“, когда адрес для PostController будет “localhost:8080/posts“. При этом некоторые методы имеют дальше приписки, которые также надо будет вводить, если мы захотим их вызвать.

Итак, пора запускать! Нажмём на зелёный треугольник в среде разработки, выбрав к запуску наш Spring-проект. У вас могут быть там записаны и другие проекты, так что убедитесь, что вы выбрали именно то, что нужно. Проект Spring можно отличить по его особому символу – зелёному весеннему листу.

Скриншот запуска Spring-проекта в среде разработки Intellij IDEA

Скриншот консоли запуска Spring-проекта в среде разработки Intellij IDEA

Ура, запустилось! Последняя строка в консоли говорит о том, что проект запустился за 5 секунд. Причём у меня указано, что Tomcat слушает именно на 8090 порту, так как порт 8080 у меня занят другим приложением, и я изменила порт в файле application.properties:

Что ж, попробуем написать запрос к контроллеру UserController в адресной строке браузера. Получим всех пользователей. Которых на данный момент 0. Как и записей.

Обращение к контроллеру Spring-проекта через адресную строку с localhost:8090/users

Пусто. Чего, в общем-то, следовало ожидать. Ведь такая же ситуация сейчас в базе данных – таблица users не имеет ни одной строки.

Как, впрочем, и таблица posts. В этом можно убедиться, обратившись к PostController запросом localhost:8090/posts. Отчего встаёт вопрос: что будем делать?

Обращение к контроллеру Spring-проекта через адресную строку с localhost:8090/posts

Давайте добавим новую строку в таблицу users:

Добавление в таблицу базы данных новой строки

Да, id = 3, так как я уже создавала двух пользователей до этого и потом удаляла. Но это неважно. Важно – что этот единственный пользователь появился в табличке. Обновим страницу с запросом localhost:8090/users:

Обращение к контроллеру Spring-проекта через адресную строку с localhost:8090/users с получением 1 JSON-объекта

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

Добавим ещё одного пользователя.

2 записи в таблице users БД

Обновляем страницу с запросом. Что на выходе? Обе строки из базы данных подъехали. Да, именно в таком виде потом front-end может оперировать этими данными, запрашивая их у back-end. Взяв эти JSON-объекты, front-end может красиво расположить список пользователей на странице, добавить возможность написать им сообщение, например, или осуществить поиск и фильтрацию. И это классно, когда ответственности разделяются. В back-end мы оперируем данными. Их представление – уже другая ответственность.

Обращение к контроллеру Spring-проекта через адресную строку с localhost:8090/users с получением 2 JSON-объектов

Как видно, массив posts у обоих пользователей пока пуст. Это потому, что в таблице posts по-прежнему 0 строк, так что ни одна запись не привязана к какому-либо из пользователей. Но если попытаться внести данные в таблицу posts, случится что-то страшное…

Дело в том, что оба класса User и Post имеют ссылки друг на друга. И если запросить объект типа User или объект типа Post, тогда начнётся рекурсия, которая закончится Exception-ом. А нам это не надо.

Как такую проблему решать? Для этого создают DTO и mapper-ы к ним. DTO – data transfer object – убирает связь на одной из сторон (со стороны User либо Post) при транспортировке данных.

Но это уже совсем другая история! Про контроллеры мы, безусловно, уже закончили.

Добавить комментарий