Погода

Проект “Погода” #

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

Что нужно знать #

Мотивация проекта #

  • Использование cookies и сессий для авторизации пользователей
  • Знакомство со Spring
  • Работа с внешними API

Функционал приложения #

Работа с пользователями:

  • Регистрация
  • Авторизация
  • Logout

Работа с локациями:

  • Поиск
  • Добавление в список
  • Просмотр списка локаций, для каждой локации отображается название и температура
  • Удаление из списка

Интерфейс приложения #

Для вёрстки предлагаю пользоваться Bootstrap 5.

Главная страница #

  • Заголовок
    • Для неавторизованных пользователей - кнопки регистрации и авторизации
    • Для авторизованных пользователей - логин текущего пользователя и кнопка Logout
  • Контент
    • Поле ввода для поиска локации по названию
    • Список добавленных локаций. Каждый элемент списка отображает название, текущую температуру и кнопку “удалить”

Страница результатов поиска локаций по названию #

Переход на эту страницу осуществляется в результате заполнения поля ввода на главной странице, либо на странице результатов поиска.

Содержимое:

  • Заголовок, такой же как на главной странице
  • Поле ввода для поиска по названию - такое же, как на главной странице, чтобы не возвращаться туда для каждого нового поиска
  • Список найденных локаций с кнопкой “добавить”. При нажатии на кнопку происходит переход на главную страницу

Остальное #

  • Страницы с формами регистрации и авторизации

Работа с сессиями и cookies #

Чтобы реализовать авторизацию пользователя и позволить браузеру “запоминать” авторизован ли текущий пользователь, необходимы сессии и cookies. Подразумевается, что студент имеет общее представление об этих понятиях.

При авторизации пользователя бэкенд приложение создаёт сессию с идентификатором, и устанавливает этот идентификатор в cookies HTTP ответа, которым приложение отвечает на POST запрос формы авторизации. К тому же, сессия содержит в себе ID авторизовавшегося юзера.

Далее, при каждом запросе к любой странице, бэкенд приложение анализирует cookies из запроса и определяет, существует ли сессия для ID из cookies. Если есть - страница рендерится для того пользователя, ID которого соответствует ID сессии из cookies.

Фреймворки, в том числе Spring, умеют управлять всем этим. Цель этого проекта - поработать с cookies и сессиями вручную, чтобы понимать, как они работают, поэтому принципиально советую не ипользовать Spring Security, Spring Session.

Spring MVC #

Какие компоненты Spring пригодятся в проекте:

  • Beans, Dependency Injection
  • Контроллеры
  • Профили
  • Интеграция с Thymeleaf, Flyway/Liquibase
  • Spring Tests
  • RestTemplate / WebClient

База данных #

В этом проекте предлагаю использовать MySQL/MariaDB/Postgres. Создавать таблицы будем не через Hibernate, как в предыдущем проекте, а с помощью инструмента миграций - Flyway/Liquibase.

Таблица Users #

КолонкаТипКомментарий
IDintАйди пользователя, автоинкремент, первичный ключ
LoginVarcharЛогин пользователя, username или email
PasswordVarcharХранить пароль в открытом виде небезопасно, лучше использовать шифрование, например BCrypt

Таблица Locations #

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

КолонкаТипКомментарий
IDintАйди локации, автоинкремент, первичный ключ
NameVarcharНазвание
UserIdintПользователь, добавивший эту локацию
LatitudeDecimalШирота локации
LongitudeDecimalДолгота локации

Таблица Sessions #

КолонкаТипКомментарий
IDVarcharАйди сессии, GUID, первичный ключ
UserIdintПользователь, для которого сессия создана
ExpiresAtDatetimeВремя истечения сессии. Равно времени создания сессии плюс N часов

Получение информации о погоде с помощью OpenWeatherMap API #

В предыдущих проектах мы писали своё API, в этом будем пользоваться чужим. Нам нужно искать локации по названию и получать погоду для локации.

OpenWeather #

Существует множество сервисов, предоставляющих API с таким функционалом, один из них - https://openweathermap.org/. Я выбрал этот вариант, потому что он позволяет бесплатно совершать 60 запросов в минуту.

Для выполнения запросов к API нужен ключ, для его получения необходимо:

  • Зарегистрироваться на https://openweathermap.org/
  • Создать бесплатный ключ с лимитом 60 запросов в минуту
  • Дождаться активации ключа (

Работа с API #

Документация - https://openweathermap.org/api.

Нам нужно 2 метода API:

Первым делом следует поэкспериментировать с API вручную, чтобы понять как делать запросы, и что приходит в ответе. Сделать это можно в HTTP клиенте встроенном в Intellij IDEA, либо воспользоваться отдельным приложением, например https://insomnia.rest/.

Интеграция OpenWeather API с Java приложением #

Шаги:

  • Делаем запрос
  • Получаем ответ
  • Десериализуем ответ в объект Java

Для работы с API потребуется HTTP Client, например https://www.baeldung.com/java-9-http-client.

С десериализацией поможет JsonMapper из библиотеки Jackson.

Тесты #

Интеграционные тесты сервисов по работе с пользователями и сессиями #

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

Пример - вызов метода регистрации в классе-сервисе, отвечающем за работу с пользователями, должен привести к тому, что в таблице Users появляется новая запись. Если при регистрации автоматически создается сессия, так же можем проверить, что она была создана.

Что ещё стоит проверить тестами:

  • Регистрация юзера с неуникальным логином приводит к exception
  • Истекание сессии

Детали:

  • Для тестов должна использоваться БД, отдельная от основной (in-memory БД или независимая schema в основной БД), которая пересоздается (или очищается) перед каждым тест кейсом
  • Понадобится 2 конфигурации приложения - основная (для разработки и деплоя) и для прогона тестов. Конфигурации могут отличаться настройками доступа к БД для Hibernate, настройками приложения (длительность сессии, например)

Интеграционные тесты для сервиса по работе с OpenWeather API #

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

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

Что ещё стоит проверить тестами:

  • В случай ошибки (статусы 4xx, 5xx) от OpenWeather API сервис выбрасывает ожидаемый тип исключения

Деплой #

Будем вручную деплоить war артефакт в Tomcat, установленный на удалённом сервере. Потребуется установка внешней SQL БД по выбору.

Шаги:

  • Локально собрать war артефакт приложения
  • В хостинг-провайдере по выбору арендовать облачный сервер на Linux
  • Установить JRE, Tomcat, выбранную SQL БД
  • Зайти в админский интерфейс Tomcat, установить собранный war артефакт

Ожидаемый результат - приложение доступно по адресу http://$server_ip:8080/$app_root_path.

План работы над приложением #

  • Создать заготовку Java бэкенд приложения с javax.servlet
  • Написать модели сущностей БД - User, Location, Session и миграции для создания таблиц
  • С помощью Thymeleaf создать страницы авторизации и регистрации
  • В обработчиках форм авторизации и регистрации реализовать бизнес логику работы с сессиями, cookies, БД
  • Интеграционные тесты для сервиса регистрации
  • Написать сервис для работы с OpenWeather API
  • Интеграционные тесты для OpenWeather API
  • Реализовать бизнес логику приложения - поиск, добавление, удаление локаций, просмотр погоды
  • Создать интерфейс главной страницы и страницы поиска локаций
  • Деплой

Ресурсы для работы над ошибками #

  • Реализации проекта другими студентами и мои ревью этих реализаций
  • Чеклист для самопроверки с типовыми ошибками (в конце страницы)
  • Целевое количество видео и текстовых ревью проекта “Погода” накоплено, новые реализации к ревью не принимаются. В любом случае призываю отправлять законченные проекты в чат, добавляю их в список. Подробности - https://t.me/zhukovsd_it_mentor/57

Чеклист для самопроверки #

❗️Спойлеры: советую не читать этот список до того момента, пока не допишете первую самостоятельную работающую версию проекта❗️

Функциональные проблемы:

  • Отсутствие валидации и визуализации ошибок при регистрации и авторизации
  • Несовпадение имени локации, по которой мы её добавляли, с тем именем, по которому она отображается, пример:
    • Ищем “Санкт-Петербург”, добавляем, сохранив в БД координаты
    • Для показа погоды делаем запрос к API по координатам, получаем погоду и имя локации. Найденная по координатам локация называется “Новая Голландия”, а не “Санкт-Петербург”
  • Невозможность отличить несколько локаций с одним названием. Пример - в мире есть несколько городов с названием “Гомель”. Возможные решения - показывать координаты локации
  • Возможность удалить локацию чужого пользователя через подмену ID в запросе

Проблемы и ошибки в коде:

  • Уязвимость к race conditions, подробно на примере разбирал здесь
  • Размытые границы между слоями MVC архитектуры приложения
  • БД и сущности:
    • Отсутствие unique индексов на поля или комбинации полей, которые должны быть уникальными. Например - логин пользователя
    • Отсутствие внешних ключей между таблицами
    • Использование @Data из Lombok на классах-сущностях потенциально опасно - https://habr.com/ru/companies/haulmont/articles/564682/
    • Излишне сложный дизайн хранения локаций пользователей (например, через many-to-many). Подробно эту тему разбирал тут https://www.youtube.com/watch?v=yLBn7qmyCOk
  • Недостаточно очевидный нейминг
  • Работа с внешним API:
    • Ручной парсинг ответов вместо использования Jackson + DTO

Общее:

  • Трудно локально запустить проект, отсутствие README
  • Ключ для OpenWeather API не должен попасть в репозиторий