Простое API на ASP.NET Core
Дмитрий БахтенковВведение
ASP.NET Core - это кроссплатформенный фреймворк для разработки веб-приложений на языках .NET, и, в частности, на C#. Существуют следующие типы шаблонов приложений asp.net core:
- Empty - пустое приложение
- Web Api - шаблон простого REST API
- Web App - приложение Razor Pages
- Web App MVC - приложение, созданное по паттерну model-view-controller
Первое приложение
Чтобы создать приложение, необходимо выбрать тип проекта asp.net core web api:

Приложение, созданное по шаблону имеет следующую структуру:

Класс Program является стартовой точкой приложения. Тут можно сконфигурировать приложение, настроить DI-контейнер (про DI рассказывалось в этой статье):

По умолчанию в приложении используется генератор документации к API Swagger, а в DI-контейнере регистрируются контроллеры из приложения.
Файлы appsettings.json и appsettings.Development.json являются файлами конфигурации для нашего приложения. По умолчанию в нём хранится информация об уровне логгирования, но в эти файлы можно также записывать различные токены, строки подключения к бд и другую конфигурационную информацию.

Файл WeaterForecast.cs описывает модель для вывода данных. Это стандартный файл для примера из шаблона ASP.NET Core Web API. Этот класс является так называемым dto - data transfer object - простой класс, который используется только для передачи данных и имеет минимум логики.

Файл Controllers/WeatherForecastController - это специальный класс контроллера. Контроллеры описывают конечные точки приложения - http-методы, к которым мы затем сможем обращаться. Каждый контроллер должен наследоваться от классов ControllerBase или Controller, которые предоставляют базовые методы по работе с данными, переданными по http, и иметь атрибуты ApiController и Route с параметрам маршрута. Параметр [controller] описывает наименование класса контроллера без постфикса Controller, в данном случае - WeatherForecast

Для описания эндпоинтов используются публичные методы с атрибутами HttpGet / HttpPost / HttpPut и др. В этих методах может содержаться различная логика по получению / преобразованию данных, и эти методы будут выполняться при обращении к эндпоинтам по http.
Попробуем запустить приложение. Оно сразу откроет страницу со сваггером, где можно посмотреть описание эндпоинтов и попробовать их выполнить

В описании нашего единственного метода мы видим, что он не принимает никаких параметров и возвращает массив с некоторой информацией.

Нажав на кнопку “Try it out” можно попробовать выполнить http-запрос на эндпоинт. В результате выполнения мы получим некоторый массив данных, как и описано в документации:

Теперь перейдём к практике. Для начала просто удалим файлы WeatherForecast.cs и WeatherForecastController.cs - они нам не пригодятся.
Практика
Надеюсь, все любят пиццу?) Как задачу мы возьмём построение API для каталога пицц в пиццерии. Мы будем хранить данные о пиццах, выводить их, создавать новые, фильтровать и т.д.
Слой данных
Для начала необходимо определить модель нашей пиццы. Создадим папку Models, и в ней класс PizzaModel. У нашей пиццы будет: уникальный идентификатор, цена, название, описание:

Для уникального идентификатора используется структура GUID - статистически уникальный 128-битный идентификатор. Подробнее о нём можно почитать в документации.
Класс модели у нас не будет иметь никакой логики, а будет просто описывать то, что хранится в нашей базе данных. Саму базу данных мы подключать не будем, но используем ORM-библиотеку Entity Framework Core, и её расширение InMemory - чтобы все данные хранились в памяти, и не нужно было устанавливать и изучать реляционную базу данных - об этом как-нибудь в отдельной статье. Её можно установить через менеджер пакетов nuget.

EF Core - это технология доступа к базам данных от Microsoft. Эта библиотека позволяет представлять объектно-ориентированные данные как таблицы в БД и наоборот, а для запросов в базу данных использовать LINQ. Подробнее об этой библиотеке можно почитать в документации
Для доступа к данным создаётся специальный класс-контекст. Этот класс является прослойкой между хранилищем данных (БД) и приложением. Создадим класс AppContext:

Класс контекста обязательно должен быть унаследован от DbContext. Для конфигурации подключения переопределяется защищённый метод OnConfiguring, и настройка происходит через параметр этого метода. В каждом провайдере БД существует метод рассширения для DbContextOptionsBuilder, например UseSqlServer, UseMySql и т.д. Так как мы используем InMemory-провайдер, для конфигурации используется метод UseInMemoryDatabase().
Свойство DbSet<> описывает конкретные таблицы в БД и привязывает к ним типы моделей. В данном случае у нас одна таблица Pizzas, привязанная к модели PizzaModel.
С помощью созданного класса мы будем управлять нашими данными - создавать, редактировать, удалять и т.д.
Чтобы использовать наш контекст в качестве зависимости, нужно зарегистрировать его в DI-контейнере. Для этого используется метод расширения AddDbContext. Зарегистрируем наш контекст в файле Program.cs:

Слой сервисов
Для написания различной логики по работе с пиццей мы будем использовать сервисы - они будут описывать стандартные CRUD-операции - Create, Read, Update, Delete.
Для начала сделаем папку Services в нашем проекте, а затем добавим класс PizzaService:

В нашем сервисе будут следующие методы:
Create(PizzaModel pizza)- добавить пиццуUpdate(PizzaModel pizza)- обновить существующую пиццуDelete(Guid id)- удалить пиццу по IdSearch(string name = null)- найти пиццу или получить все пиццыGet(Guid id)- получить пиццу по Id
Для реализации работы с данными необходимо использовать зависимость AppContext, которую мы создали ранее. Эту зависимость мы инициализируем в конструкторе сервиса:

Начнём с метода создания пиццы:

Затем - обновление:

Далее - методы получения пицц:

И метод удаления:

Также нам необходимо зарегистрировать наш сервис в DI-контейнере:

Для того, чтобы вызывать наши методы, нужно создать контроллер. Для этого добавим класс PizzaController в папку Controllers, унаследуем его от класса ControllerBase и добавим к нему атрибуты Route и ApiController:

Добавим в контроллер зависимость PizzaService:

Этот сервис мы будем использовать в наших эндпоинтах.
В HTTP существуют следующие типы запросов (на самом деле их больше, тут перечислены самые основные):
- POST - создание данных
- PUT - обновление данных
- GET - получение данных
- DELETE - удаление данных
Для привязки определённого типа запросов к методам в контроллере используются соответственно атрибуты HttpPost, HttpPut, HttpGet и HttpDelete. Каждый атрибут может принимать в качестве параметра конкретный путь - это позволяет например делать запросы одного типа на разные адреса, например api/pizza/search или просто api/pizza.
Реализуем методы для нашего контроллера:

Атрибуты [FromBody] или [FromQuery] указывает тип параметров http, откуда нужно забрать данные - из тела запроса или из query-параметров.
Теперь можно запустить и протестировать наш сервис:

Для каждого написанного метода сгенерировалась документация Swagger. Через неё мы также можем и протестировать наши методы. Попробуем создать пиццу:

Теперь найдём её с помощью метода search:

Можем также отредактировать пиццу с помощью put-метода:

Но что если мы попробуем ввести Id пиццы, которой не существует?

Возникнет ошибка, которая выведется простой некрасивой строкой.
лобальная обработка ошибок
Для выполнения некоторых действий до или после определённых стадий запроса в asp.net core существуют фильтры - подробнее о них можно почитать тут. Существуют следующие фильтры:
- Авторизации
- Ресурсов
- Действий
- Исключений
Для перехвата исключений, как уже ясно из названия, используются фильтры исключений. Чтобы создать такой фильтр, необходимо создать класс, реализующий интерфейс IExceptionFilter:

В методе OnException пишется логика, которая должна выполниться при получении необработанного исключения. Сделаем возврат кода 400 и ответа в виде { Message: “text message” }:

Для установки результата запроса необходимо использовать свойство ExceptionContext.Result, которое описывает конкретный результат запроса. Во фреймворке определено много разных типов результата - OkResult, NotFoundResult и др. Мы будем использовать класс BadRequestObjectResult - он описывает результат с кодом 400 и определённом объектом в теле ответа. В тело ответа мы поместим анонимный объект с единственным свойством Message , где будет находиться значение сообщения исключения.
Теперь нам необходимо установить наш новый фильтр. Для этого в файле Program немного изменим вызов метода AddControllers:

Этот метод принимает в параметре функцию конфигурации контроллеров. У объекта конфигурации есть свойство Filters, которое является коллекцией используемых фильтров в контроллерах. В неё необходимо добавить тип нашего нового фильтра.
Пробуем запустить наше приложение и выполнить метод обновления несуществующей пиццы:

Конец
В этой статье показаны лишь самые основы фреймворка asp.net core - мы научились делать простое API для некоторого сервиса с использованием базы данных.
Спасибо за внимание!
Исходный код можно посмотреть на гитхаб.
С вами был Flex Code