Погода

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

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

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

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

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

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

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

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

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

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

REST API #

  • Архитектурный стиль - REST для всех обработчиков
  • Все эндпоинты существуют под общим путём /api. Пути ниже относительны его, пример - /api/auth/sign-up.

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

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

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

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

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

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

Содержимое:

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

Остальное #

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

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

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

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

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

База данных #

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

Таблица Users #

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

Таблица Locations #

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

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

Таблица Sessions #

КолонкаТипКомментарий
IDUUIDАйди сессии, UUID, первичный ключ
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 вручную, чтобы понять как делать запросы, и что приходит в ответе. Сделать это можно через curl, либо воспользоваться отдельным приложением, например https://insomnia.rest/.

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

Шаги:

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

Для работы с API потребуется HTTP Client, будем использовать встроенный в go.

С десериализацией поможет пакет encoding встроенный в std.

Observability #

Для обеспечения прозрачности и отладки поведения приложения важно реализовать наблюдаемость (observability) - способность системы предоставлять достаточно информации для диагностики проблем без необходимости изменять код.

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

  • time - Время запроса в человекочитаемом формате (например, 2025-11-21T14:32:05.123+03:00 )
  • req - Метод и URI запроса (например, GET /locations), Body, если присутствует в запросе
  • resp - тело ответа на запрос
  • code - HTTP-статус-код (например, 200, 404, 500)
  • duration_ms - Время обработки запроса в миллисекундах (с точностью до двух знаков после запятой)

Фронтенд #

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

Поэтому, для проекта существует готовая верстка, которую можно взять за основу. Если хотите расширить функционал проекта или сделать верстку с нуля - приветствую желание самостоятельно это сделать.

Репозиторий с версткой - https://github.com/zhukovsd/weather-viewer-html-layouts.

Что внутри - макеты четырёх страниц, адаптивная верстка (десктопы, телефоны). Минимальный Javascript для показа меню навигации на телефонах.

Задеплоенные на GitHub Pages страницы для демонстрации:

Тесты #

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

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

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

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

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

Детали:

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

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

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

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

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

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

Деплой #

Будем вручную деплоить приложение на удалённый сервере. Шаги:

  • В хостинг-провайдере по выбору арендовать облачный сервер на Linux
  • Подключиться к серверу через SSH, установить Go и поднять postgresql
  • Используя git склонировать проект
  • Написать команду для запуска приложения в background режиме, чтобы после отключения от консоли сервера оно продолжало работать

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

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

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

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

  • Реализации проекта другими студентами и мои ревью этих реализаций
  • Чеклист для самопроверки с типовыми ошибками (в конце страницы)
  • Присылайте законченные проекты в чат, добавляю их в список, сообщество делает ревью проектов

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

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

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

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

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

  • Уязвимость к race conditions, подробно на примере разбирал здесь
  • Размытые границы между слоями MVCS архитектуры приложения
  • Большое количество дубликатов кода, например заголовки и ошибки при http запросах
  • Отсутствие логирования, что не позволяет быстро выявлять проблемы в коде
  • Сильная связанность компонентов сервиса
  • БД и сущности:
    • Отсутствие unique индексов на поля или комбинации полей, которые должны быть уникальными. Например - логин пользователя
    • Отсутствие внешних ключей между таблицами
    • Излишне сложный дизайн хранения локаций пользователей (например, через many-to-many). Подробно эту тему разбирал тут https://www.youtube.com/watch?v=yLBn7qmyCOk
  • Отказ от использования интерфейсов, для частей которые будут подмененны на моки
  • Недостаточно очевидный нейминг

Общее:

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