- минимальный API для выполнения базового CRUD

Для тех, кто хочет изучить C# и .NET Framework, Microsoft Learn — лучшее место, где можно получить все ресурсы и материалы, охватывающие большую часть области, необходимой для этого путешествия.

Однако эта статья расширила мое понимание как учащегося по настройке API для подключения к MongoDB для выполнения основных операций CRUD. В качестве примера для демонстрации шагов я буду использовать один из своих проектов, JobTracker, а используемую мной среду разработки — Visual Studio 2019.

Предпосылки:

  1. У вас установлена ​​Visual Studio в локальной среде.
  2. У вас есть фундаментальные знания языка C#.
  3. У вас есть учетная запись 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.

  1. Войдите на свой сайт MongoDB Atlas.
  2. Выберите/создайте кластер на вкладке «Службы данных».
  3. Найдите в панели управления «Просмотр коллекций», нажмите на него.
  4. Выберите опцию «Создать базу данных», которая находится прямо над списком коллекций. Появится всплывающее окно, как на скриншоте.
  5. Заполните информацию, как показано ниже, и нажмите «Создать».

Имя базы данных: JobTracker
Имя коллекции: Jobs

Вариант Б: В Монгоше

  1. Откройте терминал и введите mongosh --username
  2. Он должен запросить подробную информацию о соединении, аналогичную следующей:

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 — Создание моделей

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

  1. Добавьте новую папку, назовите ее «Модели».
  2. Добавьте новый класс, назовите его «JobModel.cs».
  3. Добавьте новый класс, назовите его «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 — Добавьте контроллер

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

  1. Создайте новый класс под названием «JobsController» внутри /Controllers.
    (У вас может возникнуть соблазн выбрать «добавить контроллер» и получить готовый к использованию шаблон, но я выберу простой класс, чтобы начать с нуля, чтобы лучше понять)
  2. Создайте конструктор с помощью следующего кода.
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 или любые подобные приложения.

  1. В встроенной PowerShell Visual Studio введите dotnet run.
  2. Проверьте терминал и скопируйте URL-адрес (должен быть локальный хост с портом).

3. Зайдите в Почтальон.

4. Вставьте URL-адрес в запрос. Установите метод «GET». Он должен вернуть вам пустой массив. Это хорошо, пока у нас не было ошибок.
(Это просто пустой массив, потому что мы не заполнили базу данных значением по умолчанию)

5. Теперь установите метод «POST». Нажмите «Body › Raw» и создайте объект json внутри текстовой области. Он должен ответить вам вновь созданной записью.

6. Если вы попытаетесь изменить метод на «PUT» и «DELETE» с правильным идентификатором в конечной точке, вы должны получить NoContent().

Заключение

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

Вы можете найти репозиторий, связанный с этой статьей, в моем Github.

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

Вам также может быть интересно посетить мою домашнюю страницу :D



дальнейшее чтение