Простое API на ASP.NET Core

Простое 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:

Создание проекта

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

Структура проекта WebApi

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

Program.cs

По умолчанию в приложении используется генератор документации к API Swagger, а в DI-контейнере регистрируются контроллеры из приложения.

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

appsettings.json

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

DTO WeatherForecast

Файл 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. У нашей пиццы будет: уникальный идентификатор, цена, название, описание:

Pizza.cs

Для уникального идентификатора используется структура 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) - удалить пиццу по Id
  • Search(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

Report Page