Перейти к содержанию

4. REST API событий

Цель

Создать контроллер EventsController и проверить CRUD-операции через Swagger или HTTP-клиент.

Объяснение

Контроллер не должен знать, как устроен EF Core. Он получает запрос, вызывает сервис и возвращает статус HTTP.

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

Представьте путь запроса POST /api/events:

  1. Клиент отправляет JSON с описанием события.
  2. EventsController принимает этот JSON.
  3. Контроллер определяет текущего пользователя. Пока авторизации нет, временно используется Id = 1.
  4. Контроллер вызывает CreateAsync.
  5. Сервис создаёт событие и передаёт его в репозиторий.
  6. Репозиторий сохраняет запись в SQLite.
  7. API возвращает созданное событие и статус 201 Created.

Именно поэтому важно не пропускать предыдущие этапы: контроллер в этом разделе опирается на уже созданные DTO, интерфейсы, сервисы и регистрацию зависимостей.

Код

Теперь добавляется HTTP-точка входа в уже готовую бизнес-логику. EventsController принимает параметры из запроса, вызывает IActivityEventService и преобразует результат работы сервиса в стандартные HTTP-ответы.

ActivityMonitoring.Api/Controllers/EventsController.cs:

using System.Security.Claims;
using ActivityMonitoring.Application.Events;
using Microsoft.AspNetCore.Mvc;

namespace ActivityMonitoring.Api.Controllers;

[ApiController]
[Route("api/events")]
public class EventsController : ControllerBase
{
    private readonly IActivityEventService _service;

    public EventsController(IActivityEventService service)
    {
        _service = service;
    }

    [HttpGet]
    public async Task<ActionResult<IReadOnlyList<ActivityEventDto>>> Get(
        [FromQuery] ActivityEventFilter filter,
        CancellationToken ct)
    {
        var result = await _service.GetAsync(filter, ct);
        return Ok(result);
    }

    [HttpPost]
    public async Task<ActionResult<ActivityEventDto>> Create(
        CreateActivityEventRequest request,
        CancellationToken ct)
    {
        var userId = GetCurrentUserIdOrDefault();
        var result = await _service.CreateAsync(request, userId, ct);
        return Created($"/api/events/{result.Id}", result);
    }

    [HttpDelete("{id:int}")]
    public async Task<IActionResult> Delete(int id, CancellationToken ct)
    {
        var deleted = await _service.DeleteAsync(id, ct);
        return deleted ? NoContent() : NotFound();
    }

    private int GetCurrentUserIdOrDefault()
    {
        var claim = User.FindFirstValue(ClaimTypes.NameIdentifier);
        return int.TryParse(claim, out var userId) ? userId : 1;
    }
}

На этом этапе авторизация ещё не включена, поэтому временно используется пользователь admin с Id = 1.

Такой временный код нужен только для учебного перехода. В следующем этапе метод GetCurrentUserIdOrDefault начнёт брать пользователя из JWT-токена. В реальной системе нельзя автоматически подставлять администратора, потому что это нарушает безопасность.

Куда вставлять код

Создайте файл именно в проекте API:

ActivityMonitoring.Api/
  Controllers/
    EventsController.cs

Если контроллер случайно создать в Application или Infrastructure, ASP.NET Core не найдёт его как HTTP-контроллер.

После добавления файла проверьте, что в Program.cs есть строки:

builder.Services.AddControllers();
app.MapControllers();

Первая строка подключает механизм контроллеров, вторая добавляет маршруты контроллеров в приложение.

После этого запустите API. Запуск нужен не только для проверки компиляции: ASP.NET Core при старте также проверит регистрацию зависимостей, маршруты контроллеров и подключение конфигурации.

dotnet run --project ActivityMonitoring.Api

Первым HTTP-запросом создайте событие. Он проверяет полный путь от контроллера до базы данных: JSON попадает в DTO, сервис создаёт ActivityEvent, репозиторий сохраняет запись, а API возвращает созданный объект.

POST http://localhost:5000/api/events
Content-Type: application/json

{
  "type": "PageView",
  "source": "swagger",
  "description": "Открыта страница аналитики",
  "severity": "Info"
}

Затем запросите список событий. Этот запрос нужен, чтобы убедиться, что созданная запись действительно сохранена и что фильтр с пагинацией корректно передаётся из query string в сервис.

GET http://localhost:5000/api/events?page=1&pageSize=10

Как понять, что всё работает

Проверка считается успешной, если выполняются три условия:

  1. POST /api/events возвращает статус 201 Created.
  2. В ответе есть поле id, которое больше нуля.
  3. GET /api/events возвращает созданное событие в списке.

Если POST сработал, но GET возвращает пустой список, проверьте, что приложение использует ту же базу данных, в которую была применена миграция.

Частые проблемы

Проблема Возможная причина Решение
Swagger не показывает контроллер Нет app.MapControllers() Добавить строку в Program.cs
Ошибка DI при запуске Не зарегистрирован сервис или репозиторий Проверить AddScoped<IActivityEventService, ActivityEventService>()
Ошибка no such table Не выполнена миграция Выполнить dotnet ef database update
В ответе User равен null Не загружена навигация User Проверить Include(x => x.User) или LoadAsync в репозитории

Результат

API умеет создавать, получать и удалять события активности. На следующем этапе эти эндпоинты будут защищены JWT-токеном.

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