July 3, 2023

Пишем полноценное веб-приложение. Часть 1 - Web API

Введение

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

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

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

Стек

При разработке в этой части будем использовать следующие технологии:

  • .NET 6 (C#)

Что будем делать

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

Это приложение для работы с фича-флагами.

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

В этой статье попробуем написать небольшое API для работы с фича-флагами.

С чего начнём

Думаю, что рассказывать о том, как создать шаблон приложения с API, не нужно. Но на всякий случай, приложу скрин, как это делается:

Получаем шаблон приложения Web API, который можно запустить и откроется Swagger (инструмент, для отладки API).

Убираем всё лишнее

Удаляем шаблонный контроллер (WeatherForecastController) и модель (WeatherForecast).

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

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

Модель данных - это набор данных, который мы можем либо принимать в контроллерах, либо отдавать из них.

Пишем свой контроллер

Создадим класс в папке Controllers, назовём его FeatureFlagsController:

Унаследуемся от ControllerBase, навесим атрибуты ApiController и Route, в котором укажем путь для всего контроллера ([controller] будет заменён на имя класса, без слова Controller, автоматически):

Добавляем методы для контроллера

Методы, они же endpoints, или "ручки" (на сленге разработчиков), это конкретные пути в контроллере, на которые будут приходить запросы.

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

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

Трёхзвенная архитектура

Трёхзвенная, она же трёхуровневая (и трёхслойная), архитектура - устройство приложения, разделяемое на три слоя (звена) - контроллеры, сервисы и репозитории.

С контроллерами мы уже знакомы, остаются сервисы и репозитории.

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

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

Отложенная реализация

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

Пишем сервис

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

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

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

Добавим этот интерфейс в наш контроллер:

И уже можем прописать логику работы контроллеров:

Разметим репозиторий

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

Настало время действительно написать сервис

Интерфейс это, конечно, хорошо, но кто работать будет? Идём реализовывать интерфейс.

Добавим поле с репозиторием:

И погнали:

Почему у нас три метода в сервисе

Метод установки значения для фича-флага очень сильно похож на метод его создания, поэтому мы можем объединить эту логику в сервисе, чтобы потом переиспользовать её в контроллере (вот нам и пригодились сервисы).

Откладываем реализацию репозитория

Пришло время реализовать слой работы с данными. Но мы же не хотим сейчас лезть в базы данных, верно?

Поэтому отложим реализацию, и напишем что-то вроде этого.

Добавим локальное хранилище (словарик):

Реализуем получения ключа для нашего хранилища:

И сами методы:

Регистрируем классы

После того, как мы всё сделали, осталось сходить в DI и зарегистрировать наши классы. Идем в файлик Program и прописываем:

Запускаем

Пробуем запустить приложение и поиграться с ним в Swagger:

Заключение

В этой статье мы разработали простое приложение, которое позволяет работать с фича-флагами.

Мы не реализовали работу с базой данных (сделаем это в следующей статье), поэтому все наши данные потеряются при перезапуске приложения.

Данный проект реализую в публичном репозитории на GitHub.