- минимальный API для выполнения базового CRUD
Для тех, кто хочет изучить C# и .NET Framework, Microsoft Learn — лучшее место, где можно получить все ресурсы и материалы, охватывающие большую часть области, необходимой для этого путешествия.
Однако эта статья расширила мое понимание как учащегося по настройке API для подключения к MongoDB для выполнения основных операций CRUD. В качестве примера для демонстрации шагов я буду использовать один из своих проектов, JobTracker, а используемую мной среду разработки — Visual Studio 2019.
Предпосылки:
- У вас установлена Visual Studio в локальной среде.
- У вас есть фундаментальные знания языка C#.
- У вас есть учетная запись MongoDB Altas.
ШАГ 1 — Базовая настройка
Сначала давайте откроем Visual Studio и в строке меню выберем «Файл › Создать › Проект..».
Вы можете увидеть следующее всплывающее окно.
С левой стороны вы выберете «ASP.NET Core Web API». Всплывающее окно предложит вам заполнить информацию для нового проекта. Назовите проект по своему усмотрению, оставьте другие поля по умолчанию и нажмите «Далее», убедитесь, что «Framework» имеет значение 7.0, а затем нажмите «Создать».
Для вашего нового проекта будет создан шаблон.
ШАГ 2 — Установите пакет: MongoDB.Driver
Нам нужно добавить пакет MongoDB.Driver для подключения к MongoDB.
https://www.nuget.org/packages/MongoDB.Driver
В Microsoft Learn предлагается использовать графический интерфейс для добавления пакетов, открыв «Инструменты > Диспетчер пакетов NuGet > Консоль диспетчера пакетов», но я бы рекомендовал остаться с терминалом / Developer PowerShell.
Убедитесь, что путь в терминале правильный, введите следующую командную строку:
dotnet add package MongoDB.Driver
Чтобы проверить, установлен ли пакет, в обозревателе решений щелкните правой кнопкой мыши «Зависимости» и выберите «Управление пакетами NuGet». Вы должны увидеть MongoDB.Driver в списке следующим образом:
Далее, перед написанием кода, вы также создадите базу данных в MongoDB для этого проекта.
ШАГ 3 — Настройка базы данных в MongoDB (Mongosh)
Вы можете создать базу данных и коллекции непосредственно на веб-сайте MongoDB Altas или, как я, подписаться на Microsoft Learn и настроить Mongosh (MongoDB Shell) в локальной среде.
Если вы хотите использовать Mongosh и не установили Mongosh, следуйте следующей статье, чтобы настроить его.
Вариант А: на веб-сайте MongoDB Atlas.
- Войдите на свой сайт MongoDB Atlas.
- Выберите/создайте кластер на вкладке «Службы данных».
- Найдите в панели управления «Просмотр коллекций», нажмите на него.
- Выберите опцию «Создать базу данных», которая находится прямо над списком коллекций. Появится всплывающее окно, как на скриншоте.
- Заполните информацию, как показано ниже, и нажмите «Создать».
Имя базы данных: JobTracker
Имя коллекции: Jobs
Вариант Б: В Монгоше
- Откройте терминал и введите
mongosh --username
- Он должен запросить подробную информацию о соединении, аналогичную следующей:
3. На снимке экрана выше mongosh подключен к localhost:27017, чего достаточно на локальной стадии разработки. Тем не менее, вы можете изменить его для подключения к кластеру в Altas.
(Если вы уже подключались к Altas, вы можете пропустить шаги 4–6)
4. Войдите в MongoDB Atlas.
5. На панели управления развертыванием базы данных нажмите «Подключиться», которая находится рядом с именем кластера (предположим, вы не хотите создавать новый кластер), выберите «Драйвер» в списке, чтобы открыть следующее окно. Скопируйте строку подключения, начинающуюся с mongodb+srv://
.
6. Вернитесь к терминалу и убедитесь, что вы вышли из mongosh после шага 3. Введите mongosh <connection string>
и не забудьте заменить ‹имя пользователя›, ‹пароль› своими собственными учетными данными.
7. В mongosh введите show dbs
, чтобы вывести список всех баз данных.
8. Введите use <database_name>
, чтобы войти в базу данных (и создать ее, если она не существует) для нового проекта. Назовем базу данных «JobTracker».
9. Внутри базы данных введите use <collection_name>
, чтобы войти в коллекцию (и создать ее, если она не существует). Назовем коллекцию «Работы».
P.S. Вам необходимо создать как минимум коллекцию, даже без каких-либо записей, чтобы вновь созданная база данных сохранилась.
ШАГ 4. Давайте напишем код!
Теперь, когда у нас есть новый проект с шаблоном API, открытым в Visual Studio, а также настроена база данных, мы можем приступить к написанию кода! Структура API такая:
Шаг 4.1 — Создание моделей
Для начала создадим модели. Модели описывают, как должны выглядеть данные, которыми мы манипулируем.
- Добавьте новую папку, назовите ее «Модели».
- Добавьте новый класс, назовите его «JobModel.cs».
- Добавьте новый класс, назовите его «DatabaseSetting.cs».
JobModel.cs
Внутри публичного класса с тем же именем, что и у файла, добавьте конструктор свойств документа, как показано ниже:
using MongoDB.Bson.Serialization.Attributes; namespace JobTracker.Models { public class JobModel { [BsonId] [BsonRepresentation(MongoDB.Bson.BsonType.ObjectId)] public string? Id { get; set; } public string Position { get; set; } = null!; public string? Requirement { get; set; } public string Company { get; set; } = null!; public string Location { get; set; } = null!; public string? Contact { get; set; } public string Email { get; set; } = null!; public string Date { get; set; } = DateTime.UtcNow.ToString(); [BsonRepresentation(MongoDB.Bson.BsonType.DateTime)] public DateTime DateApplied => DateTime.Parse(Date); public string Status { get; set; } = "pending"; } }
[BsonId]
указывает свойство как первичный ключ документа.[BsonRepresentation(MongoDB.Bson.BsonType.ObjectId]
позволяет свойству быть строкой и передавать его в MongoDB для преобразования в ObjectId на своей стороне.- Убедитесь, что
using MongoDB.Bson.Serialization.Attributes
разрешены указанные выше аннотации. <T>?
указывает, что свойство имеет значение NULL.- Если свойство не допускает значения NULL, прикрепите
= null!
, чтобы указать, что оно не должно иметь значение NULL, или= <default_value>
.
Настройка базы данных.cs
Затем вы создадите модель конфигурации базы данных, которая содержит следующие 3 свойства, все из которых не допускают значения NULL:
namespace JobTracker.Models { public class DatabaseSetting { public string ConnectionString { get; set; } = null!; public string DatabaseName { get; set; } = null!; public string CollectionName { get; set; } = null!; } }
ШАГ 4.2 — Обновите appsettings.json
Давайте добавим пользовательский раздел со значением конфигурации базы данных в appsettings.json:
{ // You can name the properties however you want as long as you are consistent // You can find the connection string in Step 3 > Option B > Step 4-6 "JobTrackerDatabase": { "ConnectionString": <connection_string>, "DatabaseName": "JobTrackerDB", "CollectionName": "Jobs" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
ШАГ 4.3 — Обновите Program.cs
Program.cs — это точка входа всего приложения. Он должен был быть заполнен какими-то кодами. Найдите пространство между объявлением строителя и приложением и добавьте следующую строку:
builder.Services.Configure<DatabaseSetting>(builder.Configuration.GetSection("JobTrackerDatabase"));
Предыдущую строку можно рассматривать как builder.Services.Configure<Database_config_model>(<Database_config_value>)
.
Поскольку Database_config_value
находится в файле appsettings.json, вы будете использовать builder.Configuration.GetSection(<customSection_name>)
для получения значений.
Это значит связать раздел «JobTrackerDatabase» с классом «DatabaseSetting», зарегистрировать его внутри контейнера внедрения зависимостей (DI) в Program.cs.
Наконец, убедитесь, что пространство имен Models используется в Program.cs (обычно оно должно добавляться автоматически благодаря intellisense).
using JobTracker.Models;
ШАГ 4.4 — Создание сервисов для CRUD
На данный момент вы настроили все для базы данных и зарегистрировали ее в контейнере DI, чтобы она была доступна для использования в любом месте приложения. Давайте создадим несколько функций для операций CRUD.
Создайте в корне папку с именем «Services», добавьте новый класс с именем «JobTrackerService».
4.4.1 Получение экземпляра базы данных из DI-контейнера
Поместите следующее в область действия класса:
using JobTracker.Models; using Microsoft.Extensions.Options; using MongoDB.Driver; namespace JobTracker.Services { public class JobTrackerService { public readonly IMongoCollection<JobModel> _jobsCollection; public JobTrackerService(IOptions<DatabaseSetting> jobTrackerDatabaseSetting) { var mongoClient = new MongoClient(jobTrackerDatabaseSetting.Value.ConnectionString); var mongoDatabase = mongoClient.GetDatabase(jobTrackerDatabaseSetting.Value.DatabaseName); _jobsCollection = mongoDatabase.GetCollection<JobModel>(jobTrackerDatabaseSetting.Value.CollectionName); } } }
В приведенном выше коде у вас есть общедоступное поле только для чтения в IMongoCollection<JobModel>
type с именем «_jobsCollection» без инициализации и конструктор, принимающий параметр в IOptions<DatabaseSetting>
type, который доступен благодаря регистрации в контейнере DI.
Внутри конструктора объявлены 3 переменные:
mongoClient:
Инициируйте новый экземпляр mongoClient
, приняв значение <parameter>.Value.ConnectionString
. Это точка входа в кластер MongoDB.
mongoDatabase:
Доступ к базе данных с помощью метода mongoClient GetDatabase
, принимающего значение <parameter>.Value.DatabaseName
.
_jobCollection:
Поле, которое вы только что объявили, для доступа к коллекции с помощью метода mongoDatabase GetCollection
, принимающего значение <parameter>.Value.CollectionName
.
Выполнив описанные выше шаги, вы успешно инкапсулировали коллекцию, чтобы защитить ее от прямого редактирования.
4.4.2 Создание функций CRUD для взаимодействия с базой данных
В блоке IOption‹DatabaseSetting› добавьте несколько методов для CRUD:
public async Task<List<JobModel>> GetAllEntries() => await _jobsCollection.Find(_ => true).ToListAsync(); public async Task<JobModel?> GetEntryById(string id) => await _jobsCollection.Find(x => x.Id == id).FirstOrDefaultAsync(); public async Task<List<JobModel>> GetEntryByLocation(string location) => await _jobsCollection.Find(x => x.Location == location).ToListAsync(); public async Task CreateEntry(JobModel newJob) => await _jobsCollection.InsertOneAsync(newJob); public async Task UpdateEntry(string id, JobModel updatedJob) => await _jobsCollection.ReplaceOneAsync(x => x.Id == id, updatedJob); public async Task RemoveEntry(string id) => await _jobsCollection.DeleteOneAsync(x => x.Id == id);
ШАГ 4.5 — Обновите Program.cs
Давайте также зарегистрируем «JobTrackerService» в контейнере DI, чтобы к нему могли обращаться другие части приложения. На этот раз вы используете AddSingleton<TService>()
;
(Что такое синглтон? Я положил вики для дальнейшего чтения :D)
builder.Services.AddSingleton<JobTrackerService>();
ШАГ 4.6 — Добавьте контроллер
Наконец, вы создадите контроллер, чтобы назначить, какая конечная точка и какой метод будут доступны, а также их работа.
- Создайте новый класс под названием «JobsController» внутри /Controllers.
(У вас может возникнуть соблазн выбрать «добавить контроллер» и получить готовый к использованию шаблон, но я выберу простой класс, чтобы начать с нуля, чтобы лучше понять) - Создайте конструктор с помощью следующего кода.
using JobTracker.Models; using JobTracker.Services; using Microsoft.AspNetCore.Mvc; namespace JobTracker.Controllers; [ApiController] [Route("api/[controller]")] public class JobsController: ControllerBase { private readonly JobTrackerService _jobTrackerService; public JobsController(JobTrackerService jobTrackerService) { _jobTrackerService = jobTrackerService; } }
Объяснение:
Сразу после пространства имен вы добавляете класс ControllerBase с public class JobsController: ControllerBase {}
.
Он содержит 2 аннотации: [ApiController] и [Route(‹endpoint›)].
С атрибутом ApiController вы включаете специальные функции, такие как автоматический 400 без ручной обработки и многое другое. (Подробнее: https://learn.microsoft.com/en-us/aspnet/core/web-api/?view=aspnetcore-7.0#apicontroller-attribute)
Вы также устанавливаете контроллер для обработки запросов от конечной точки «api/jobs», поскольку контроллер называется «JobsController».
Затем в рамках класса вы объявляете поле для службы, которую будете использовать, в данном случае JobTrackerService (помните? инкапсуляцию).
После этого напишите конструктор, чтобы назначить зарегистрированную службу из шага 4 значением поля.
3. вы добавите в конструктор следующие методы CRUD.
// api/jobs [HttpGet] public async Task<List<JobModel>> GetAll() => await _jobTrackerService.GetAllEntries(); // api/jobs/{id} [HttpGet("{id:length(24)}")] public async Task<ActionResult<JobModel>> GetById(string id) { var job = await _jobTrackerService.GetEntryById(id); if (job == null) { return NotFound(); } return job; } // api/jobs/filter?location={location} [HttpGet("filter")] public async Task<ActionResult<List<JobModel>>> GetByLocation(string location) { var jobList = await _jobTrackerService.GetEntryByLocation(location); if (jobList.Count == 0) { return NotFound(); } return jobList; } // api/jobs [HttpPost] public async Task<IActionResult> Post(JobModel newJob) { await _jobTrackerService.CreateEntry(newJob); return CreatedAtAction(nameof(GetById), new {id = newJob.Id }, newJob); } // api/jobs/{id} [HttpPut("{id:length(24)}")] public async Task<IActionResult> Put(string id, JobModel updatedJob) { var job = await _jobTrackerService.GetEntryById(id); if (job is null) { return NotFound(); } updatedJob.Id = job.Id; await _jobTrackerService.UpdateEntry(id, updatedJob); return NoContent(); } // api/jobs/{id} [HttpDelete("{id:length(24)}")] public async Task<IActionResult> Delete(string id) { var job = await _jobTrackerService.GetEntryById(id); if (job is null) { return NotFound(); } await _jobTrackerService.RemoveEntry(id); return NoContent(); }
Объяснение:
Вы создали 3 метода GET, 1 POST, 1 PUT и 1 DELETE из предыдущего кода, используя аннотации [HttpGet],[HttpPost],[HttpPut],[HttpDelete]
.
- Все пути начинаются с
api/jobs
, поскольку они находятся под одной и той же базой контроллеров. - Они асинхронны из-за взаимодействия с базой данных.
- Тип класса Task‹TResult› используется для представления асинхронной операции, возвращающей значение.
- Выберите правильный тип TResult, например ActionResult, HttpResults, IActionResult. (Подробнее: https://learn.microsoft.com/en-us/aspnet/core/web-api/action-return-types?view=aspnetcore-7.0)
- Выберите правильный тип данных для возвращаемого значения. Например, если это коллекция, вы можете выбрать List или IEnumerable.
- Вы можете использовать
NoContent(), NotFound(), Ok()
для реагирования на различные ситуации. (BadRequest()
не требуется, поскольку вы использовали атрибут ApiController вверху)
ШАГ 5 — Тестирование API с помощью Postman
Давайте проверим, работает ли API. Здесь я использую Postman, но вы также можете использовать браузер напрямую, Insomnia или любые подобные приложения.
- В встроенной PowerShell Visual Studio введите
dotnet run
. - Проверьте терминал и скопируйте URL-адрес (должен быть локальный хост с портом).
3. Зайдите в Почтальон.
4. Вставьте URL-адрес в запрос. Установите метод «GET». Он должен вернуть вам пустой массив. Это хорошо, пока у нас не было ошибок.
(Это просто пустой массив, потому что мы не заполнили базу данных значением по умолчанию)
5. Теперь установите метод «POST». Нажмите «Body › Raw» и создайте объект json внутри текстовой области. Он должен ответить вам вновь созданной записью.
6. Если вы попытаетесь изменить метод на «PUT» и «DELETE» с правильным идентификатором в конечной точке, вы должны получить NoContent().
Заключение
В этой статье рассматривается только минимальная структура API с одной конечной точкой и базовыми операциями CRUD; однако вы можете расширить его и создать API с несколькими конечными точками, принимающими несколько запросов.
Вы можете найти репозиторий, связанный с этой статьей, в моем Github.
Если у вас возникли другие проблемы, связанные с этой статьей, оставьте комментарий ниже.
Вам также может быть интересно посетить мою домашнюю страницу :D