Любой веб-разработчик знает, что новые приложения все больше ориентируются на API. Как видно на примерах RESTful и GraphQL, современные шаблоны используют внутренний API для предоставления данных и включают интерфейсное приложение (с использованием, среди прочего, React или Angular ). Все модные технологии направлены на улучшение и облегчение жизни разработчиков, максимально упрощая создание веб-API, и .NET Core не является исключением, как и вы В этой статье мы увидим третью статью из серии .NET Core - это сексуально (и вы должны это знать).

Эта статья является частью серии о .NET Core и о том, как теперь он так же просто и эффективен, как и любой другой стек, такой как Node или Rails, для создания современных приложений.

.NET Core - это круто, и вы должны это знать

.NET Core - это сексуально - приложение командной строки

.NET Core - это круто - создание веб-API

В предыдущей части мы создали многоплатформенное консольное приложение для обработки нашего курирования, добавляя темы, перечисляя их и удаляя завершенную тему. Мы создали красивую и простую архитектуру, используя некоторое разделение между доступом к базе данных - через Entity Framework с Sqlite - и самим приложением. Мы также потратили время на создание простой, но полезной абстракции для базы данных, позволяющей нам легко писать тесты и соответствовать некоторым шаблонам чистого кода.

TL; DR
Если вы хотите увидеть окончательную реализацию, вот репозиторий готового веб-API: https://gitlab.com/jbuisson/curator/tree/webapi

Начнем с самого начала

git clone --branch console [email protected]:jbuisson/curator.git
cd curator
dotnet build

Используя эти команды, мы можем продолжить разработку именно с того места, где остановились в предыдущей части. Сначала мы клонируем репозиторий Gitlab, используя ветку git с именем console. Затем мы собираем весь проект на основе файла решения (.sln) в корне. проекта.

Вот что у вас должно получиться с точки зрения дерева папок:

├── data
├── src
     ├── Curator.Console
     └── Curator.Data
         ├── Curator.Data.Entities
         └── Curator.Data.EntityFramework
             ├── Curator.Data.EntityFramework.Context
             ├── Curator.Data.EntityFramework.Memory
             └── Curator.Data.EntityFramework.Sqlite
└── tests
    ├── Curator.Console.Tests
    ├── Curator.Data.Entities.Tests
    └── Xunit.AssertExtensions

Теперь мы добавим новый проект Curator.Api, в котором мы будем создавать наш веб-API.

Минимальный жизнеспособный API

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

В первой статье этой серии мы увидели, как самостоятельно размещать веб-сервер и обрабатывать http-запросы. Теперь мы сделаем то же самое, но с использованием ASP.NET Core MVC для управления маршрутизацией, контроллерами и всем тем, что мы не хотим делать сами.

dotnet new console -o src/Curator.Api
cd src/Curator.Api
dotnet add package Microsoft.AspNetCore
dotnet add package Microsoft.AspNetCore.Mvc

Перейдите в файл Program.cs и настройте наш веб-хостинг:

В нашем недавно созданном проекте Curator.Api запустите приложение с помощью команды dotnet run. Эта команда должна запустить сервер и по умолчанию прослушивать любые запросы на http: // localhost: 5000. Однако мы не настроили контроллеры, поэтому все запросы будут выдавать ошибку 404 not found. Ниже приведен пример использования cURL в новом терминале:

curl -i http://localhost:5000
> HTTP/1.1 404 Not Found
> Date: Sun, 08 Sep 2019 12:01:57 GMT
> Server: Kestrel
> Content-Length: 0

Давайте теперь добавим первый контроллер для обработки элементов курирования. Этот контроллер будет использовать наш CuratorContext для взаимодействия с Items базы данных Sqlite, поэтому нам нужно начать с добавления ссылок на наши уже существующие проекты .

dotnet add reference ../Curator.Data/Curator.Data.Entities
dotnet add reference ../Curator.Data/Curator.Data.EntityFramework/Curator.Data.EntityFramework.Context
dotnet add reference ../Curator.Data/Curator.Data.EntityFramework/Curator.Data.EntityFramework.Sqlite

Далее, вот код самого контроллера в файле Program.cs:

Важными строками кода здесь являются атрибуты класса и метода, определяющие URL и необходимые нам параметры запроса.

[Маршрут (items)] объявляет конечную точку контроллера. Все методы действий в этом контроллере будут унаследованы от этого URL, как и для всего класса контроллера.

[HttpGet] объявляет, что следующий метод доступен только для HTTP-запроса GET. Путь маршрутизации не определен, поэтому он будет в корневом пути контроллера: / items.

[HttpPost] выполняет то же действие, что и [HttpGet], но для HTTP-запросов POST.

[FromBody] позволяет нам добавить строковый параметр к методу Post (), который будет анализироваться из тела запроса. ASP.NET Core будет обрабатывать формат за нас в зависимости от запроса Content-Type.

[HttpDelete ({id: int})] объявляет, что следующий метод доступен только для HTTP-запросов DELETE, и добавляет именованный параметр к пути, который должен быть целым числом и будет быть доступным по его имени в методе Delete ().
Пример пути: / items / 42 .

Наконец, нам нужно добавить некоторую конфигурацию для источника данных Sqlite, используя файл appsettings.json, точно так же, как мы сделали для команды линейное приложение. Просто скопируйте файл из Curator.Console в Curator.Api.

Снова запустите сервер с помощью команды dotnet run, а в другом терминале используйте cURL, чтобы поиграть с API на элементы конечная точка:

curl -i http://localhost:5000/items
> HTTP/1.1 200 OK
> Date: Sun, 08 Sep 2019 12:09:48 GMT
> Content-Type: application/json; charset=utf-8
> Server: Kestrel
> Transfer-Encoding: chunked
> []

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

curl -X POST http://localhost:5000/items \
   -H 'Content-Type: application/json' \
   -d '"medium.com/@jbuisson"'

Не забудьте указать HTTP-заголовок Content-Type, чтобы ASP.NET Core знал, как анализировать данные тела. Теперь вы можете получить созданный предмет. Я использую jq для уточнения вывода JSON, но это совершенно необязательно:

curl http://localhost:5000/items | jq
[
  {
    "id": 1,
    "name": "medium.com/@jbuisson",
    "createdAt": "2019-09-08T12:10:16.0666878"
  }
]

Если вы не хотите использовать cURL, вы можете использовать такие инструменты, как Postman для выполнения запросов к API.

Поздравляем! Мы создали рабочий веб-API с помощью всего нескольких строк очень простого кода!

Здесь я добровольно написал весь код вручную и не использовал никаких помощников. Я хотел без всякого волшебства показать вам, как создать веб-API в .NET Core. Однако есть более простой и лучший способ сделать это.

Простая настройка API

Точно так же, как мы создали наше первое приложение, мы можем использовать интерфейс командной строки (CLI) dotnet для создания хорошего веб-API. Это может разбить вам сердце, но удалите весь код, который мы только что написали. Удалите папку Curator.Api и замените ее новым проектом api с нуля.

dotnet new webapi -o Curator.Api

В этом новом проекте уже есть несколько новых файлов:

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

Startup.cs - новый файл. Здесь мы устанавливаем и настраиваем наш api в классе Startup. Большая часть кода здесь явная, и комментариев по умолчанию со ссылкой на документацию должно быть достаточно, если у вас есть какие-либо вопросы.

appsettings. **. json - это файлы конфигурации. У вас есть конфигурация по умолчанию и конфигурация переопределения для среды выполнения Development. Среды объявлены в следующем файле.

Properties / launchSettings.json содержит конфигурацию запуска. Здесь вы найдете настроенную среду и свойства запуска приложения. Часть настроек используется для Visual Studio (не VS Code) и может быть просто проигнорирована (например, профиль ISS Express).

Controllers / ValuesController.cs - это образец контроллера с фиксированными значениями, чтобы показать, как создавать свои собственные. Вы заметите несколько отличий от созданного нами контроллера:

Теперь у вас должна быть возможность создать свой собственный ItemsController на основе того, который мы создали ранее, и ValuesController интерфейса командной строки. создан для нас.

Теперь у нас есть два приложения, первое - это наше приложение командной строки, а второе - наш недавно созданный веб-API. Итак, проблема, с которой мы столкнулись, заключается в том, что мы продублировали логику как в Curator.Console / Commands, так и в Curator.Api / Controllers. Дублирование кода не только плохо (для поддержки, отладки и т. Д.), Но может быть опасно для вашего приложения. Итак, как вы уже догадались, сейчас ...

Время рефакторинга

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

Сначала мы создадим еще один отдельный проект вместе с Curator.Api и Curator.Console, который будет называться Curator.Core.

dotnet new classlib -o Curator.Core

В этом проекте замените сгенерированный класс нашим собственным сервисом:

Не забудьте добавить ссылки:

dotnet add reference ../Curator.Data/Curator.Data.Entities
dotnet add reference ../Curator.Data/Curator.Data.EntityFramework/Curator.Data.EntityFramework.Context

Я также создал настраиваемое исключение для обработки не найденных службой элементов.

Уделим время рассмотрению всех созданных нами классов и их отношения друг к другу.

  • CuratorContext - это контекст EntityFramework, определяющий наше хранилище данных.
  • DesignTimeDbContextFactory - поставщики CuratorContext, настраивающие его для баз данных InMemory и Sqlite.
  • ItemsService - это служба, использующая CuratorContext для взаимодействия с базой данных, не зная, какой поставщик мы используем.
  • ItemsController является контроллером ASP.NET Core и должен использовать ItemsService для предоставления метода этой службы в веб-API.
  • CuratorCommand - это определение команды для нашего существующего консольного приложения. Он также должен использовать ItemsService, как мы должны использовать ItemsController.

Чтобы правильно настроить все это, мы будем использовать механизм Внедрение зависимостей, уже имеющийся в .NET Core.

Давайте обновим наше уже существующее консольное приложение, используя CommandLineUtils (от «» Нейта Макмастера) с внедрением зависимостей:

В этом приложении мы используем ItemsService с реализацией Sqlite CuratorContext. Теперь вам нужно обновить команды, используя ItemsService вместо CuratorContext.

Теперь мы можем сделать то же самое в нашем проекте Curator.Api, используя класс Startup для настройки приложения. Просто добавьте следующие строки кода в метод ConfigureServices (…):

services.AddScoped<CuratorContext>(provider => new DesignTimeDbContextFactory(m_configuration).CreateDbContext(null));
services.AddTransient<ItemsService>();

Наша окончательная реализация ItemsController теперь должна выглядеть так:

Единственное, что осталось сделать, прежде чем рассматривать наше приложение как Production Ready, - это иметь надлежащую базу данных, которую могли бы использовать несколько серверов, на которых размещено наше приложение (например, за балансировщиком нагрузки). доступ одновременно. Кроме того, Sqlite стремится обеспечить локальное хранилище данных для отдельных приложений или устройств и не конкурирует с базами данных клиент / сервер, поэтому мы собираемся использовать вместо него MySql. .

Добавление нового поставщика базы данных

У нас уже есть два поставщика баз данных, один из которых использует Sqlite, а другой - InMemory в целях тестирования. После этого будет легко создать новый, но с использованием MySql.Data.EntityFrameworkCore вместо Microsoft.EntityFrameworkCore.Sqlite.

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

dotnet new classlib -o src/Curator.Data/Curator.Data.EntityFramework/Curator.Data.EntityFramework.Mysql

Затем во вновь созданном проекте нам нужно добавить ссылки и пакеты для конфигураций и EntityFramework, чтобы наконец создать реализацию DesignTimeDbContextFactory:

cd src/Curator.Data/Curator.Data.EntityFramework/Curator.Data.EntityFramework.Mysql
dotnet add package Microsoft.Extensions.Configuration
dotnet add package Microsoft.Extensions.Configuration.Json
dotnet add package MySql.Data.EntityFrameworkCore

Не забудьте добавить файл appsettings.json, в котором вы должны поместить ConnectionString на сервер Mysql, который вы используете. Он понадобится нам как во вновь созданном проекте Mysql, так и в проекте Web API.

В предыдущей статье из этой серии я рекомендовал вам четко разделить объекты, реализацию контекста и поставщика Sqlite. Теперь вы должны понимать преимущества этого разделения, имея третьего провайдера (не забывайте InMemory для тестов). Теперь даже лучше, что у нас есть выделенная и обособленная служба ItemsService в проекте Curator.Core. Мы настроили приложение Console для использования поставщика Sqlite и приложение Web Api для использования Mysql, ничего не меняя с помощью логики ItemsService!

Это основные истинные преимущества использования абстракции кода и разделения кода в наших реализациях.

Теперь нам нужен сервер Mysql для нашей локальной среды, и для этого мы будем использовать Docker. Если вы не знакомы с Docker, я действительно рекомендую вам прочитать учебник или следовать информации для начала.

Если вы предпочитаете, вы, конечно, можете использовать стандартную установку Mysql на локальном компьютере или даже общедоступный экземпляр на AWS RDS, если хотите.

Если вы еще не установили Docker, вот ссылки для установки на Mac, Ubuntu и Windows. После этого у вас должен быть доступ к инструменту docker-compose, который мы будем использовать для создания нашего контейнера Mysql.

Создайте файл с именем docker-compose.yml в корне проекта, содержащий следующий код YML:

Вы должны иметь возможность запустить контейнер следующим образом:

docker-compose up -d

Если вы следовали инструкциям, вам просто нужно обновить приложение Web API, чтобы использовать Mysql вместо Sqlite, для этого вы можете просто обновить Curator.Api.csproj и замените Sqlite на Mysql в блоке ProjectReference. , не забудьте ConnectionString в файле appsettings.json.

На этом пока все! Мы здесь уже многого достигли. Мы обновили наше консольное приложение как веб-API и провели некоторый рефакторинг, чтобы очистить его и использовать механизм внедрения зависимостей, чтобы использовать все преимущества реализованной нами архитектуры. Мы даже создали новую базу данных с рабочей локальной средой с помощью Docker.



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