Разработка API

API — это граница HTTP службы Lino. Они раскрывают варианты использования уровня приложения, не смешивая проблемы транспорта с правилами домена. В этой главе объясняется, как Lino генерирует Minimal APIs, контракты запросов/ответов, typed clients, метаданные OpenAPI и автоматическую регистрацию endpoints генераторами источников.


Сгенерированный endpoint должен оставаться тонким: получает данные HTTP, собирает команду или запрос, отправляет ее на уровень приложения через ISender и преобразует результат в typed response. Проверка, бизнес-правила, сохранение, транзакции и авторизация домена продолжаются в командах, запросах, обработчиках, валидаторах, репозиториях и Unit of Work.

Minimal APIs на Lino

Lino использует ядро ​​ASP.NET Minimal APIs в качестве стандарта для создания endpoints, поскольку они сохраняют код прямым, явным и производительным, не нарушая границ Clean Architecture.

В текущих проектах Lino сгенерированные endpoints представляют собой минимальные классы API, реализующие IEndpoint. Каждая endpoint предоставляет статический метод MapEndpoint(IEndpointRouteBuilder app) и метод обработчика, который использует ISender для отправки сгенерированной команды или запроса на уровень приложения.

Сгенерированные основные части

  • *Endpoint.cs: метод карт HTTP, маршрут, метаданные, авторизация, требования клиента, ограничение скорости и версия.
  • *Request.cs: представляет входные данные, поступающие из тела, строки запроса, параметров маршрута или данных формы.
  • *Extensions.cs: преобразует запрос в команду/запрос и преобразует результат приложения в ответ HTTP.
  • *Response.cs: определяет строго типизированный контракт, возвращаемый endpoint и typed clients.

Встроенные функции

Lino использует TypedResults и Results<...> чтобы ответы об успехе и ошибках были явными в коде и правильно отображались в OpenAPI. Ошибки, поступающие с прикладного уровня, преобразуются в ProblemDetails помещать result.MapToProblemDetails().

  • WithTags: группирует endpoints по модулю, объекту или функции в OpenAPI и Scalar.
  • WithName: устанавливает согласованный идентификатор операции, если это применимо.
  • WithSummary: четко описывает назначение endpoint.
  • Produces(statusCode, schema): указывает коды состояния и контракты ответа.
  • MapToApiVersion(1, 0): связывает endpoint с версией API.
  • RequirePermission, RequireAuthorization, AllowAnonymous и RequireTenant: применить безопасность в соответствии с параметрами проекта.
  • Ограничение скорости на уровне группы или endpoint с использованием политик аутентификации или анонимности в зависимости от конфигурации.

Запросы и ответы

По умолчанию Lino использует records для запросов и ответов. Они краткие, неизменяемые по умолчанию и хорошо работают как явные контракты между API, typed clients и внешними потребителями.

  • Запросы, полученные через endpoint, преобразуются в команды или запросы.
  • Результаты команд или запросов преобразуются в ответы.
  • Объекты домена не должны быть напрямую представлены как контракт HTTP.
public record CreatePersonRequest(string Name, int Age);
public record CreatePersonResponse(Guid Id, string Name, int Age);

Создание новых API

Создайте API с помощью CLI, когда модель предметной области и вариант использования уже достаточно ясны, чтобы их можно было раскрыть с помощью HTTP. API подключают команды и запросы к уровню представления через Minimal APIs, контракты, typed responses и метаданные документации.

lino api new
lino api new --name <ApiName> --service <ServiceName> --module <ModuleName> --entity <EntityName>
lino api list --service <ServiceName> --module <ModuleName> --entity <EntityName>

Интерактивный мастер предложит:

  • Услуга: сервис, в котором будет создан API.
  • Модуль: сервисный модуль, если применимо.
  • Сущность или перечисление: элемент домена, связанный с endpoint.
  • Имя API: обычно соответствует глаголу операции, например CreatePerson.
  • Тип операции: GET, POST, PUT, PATCH, DELETE, upload, download или endpoint опций endpoint.
  • Маршрут:шаблон маршрута, например /people/{id:guid}.
  • Характеристики: поля запроса и ответа, которые фактически должны пересекать границу HTTP.

Что может настроить мастер

  • ПОЛУЧАТЬ: одиночный результат, список, постраничный список и параметры/выбор сценариев.
  • ПОЧТА: создание и бизнес-действия, включая тип команды и выбранные свойства.
  • ПОМЕЩАТЬ и ПЛАСТЫРЬ: полное или частичное обновление.
  • УДАЛИТЬ: удаление сопоставлено с командами удаления.
  • Загрузить: endpoints с IFormFile и DisableAntiforgery() когда это необходимо.
  • Download: endpoints возвращают FileStreamHttpResult; Проекты, прошедшие проверку подлинности, могут создавать endpoint токена для безопасного доступа к файлам.
  • Перечисления: endpoints, которые предоставляют допустимые параметры для каждого сгенерированного запроса и ответа.

Рекомендуемый расход

  1. Создайте или просмотрите сущность, команды и запросы, которые представляют вариант использования.
  2. Бегать lino api new и выберите правильный сервис, модуль и объект.
  3. Выбирайте тип операции в зависимости от варианта использования, а не только по желаемому глаголу HTTP.
  4. Определите стабильный маршрут, используя такие ограничения, как {id:int} или {id:guid} когда это уместно.
  5. Выбирайте только те свойства, которые должны пересекать границу HTTP.
  6. Определите авторизацию, разрешение, требования tenantа, ограничение скорости и ожидаемые коды состояния.
  7. Запустите сборку и проверьте Scalar/OpenAPI, чтобы подтвердить маршрут, сводку, версию, безопасность и полученные ответы.

Пример: CreatePerson

При создании API POST вызов CreatePerson, связанный с сущностью Person, CLI генерирует endpoint, контракты запроса/ответа, расширения сопоставления, метаданные OpenAPI и интеграцию с соответствующей командой.

<ProjectName>/
└── src/
    └── Services/
        └── <ServiceName>/
            └── Api/
                └── Endpoints/
                    └── People/
                        └── CreatePerson/
                            ├── CreatePersonEndpoint.cs
                            ├── CreatePersonExtensions.cs
                            ├── CreatePersonRequest.cs
                            └── CreatePersonResponse.cs

Контракты и typed clients

Если в проекте есть веб-приложение Blazor, Lino также генерирует артефакты для typed consumption API: общие контракты, клиентский интерфейс и реализацию HTTP. Это позволяет Blazor использовать endpoints простым, последовательным и строго типизированным способом.

<ProjectName>/
└── src/
    └── Services/
        └── <ServiceName>/
            ├── Api.Contracts/
            │   └── Features/
            │       └── People/
            │           ├── CreatePerson/
            │           │   ├── CreatePersonRequest.cs
            │           │   └── CreatePersonResponse.cs
            │           └── IPersonApiClient.cs
            └── Api.Client/
                └── Features/
                    └── PersonApiClient.cs

Клиентский интерфейс зарегистрирован для внедрения зависимостей, и реализация использует HttpClientProvider с помощью помощников Tolitech HTTP для вызова сгенерированного API строго типизированным способом.

Контрольный список перед публикацией

  • Использовать GET для чтения, POST для создания/действий, PUT/PATCH для перемен и DELETE для удаления, когда это имеет смысл.
  • Не предоставляйте объекты домена напрямую как внешний контракт.
  • Проверка документа, конфликт, ошибки «не найден» и «отказ в доступе».
  • Использовать api list чтобы избежать дублирования маршрутов или конкурирующих endpoints для одного и того же варианта использования.

Регистрация endpoint с помощью source generators

Lino позволяет избежать ручного сопоставления больших файлов с помощью source generators. Вместо перечисления endpoints одну за другой в Program.cs, журнал создается во время компиляции.

Созданные классы endpoints реализуют IEndpoint из Tolitech.MinimalApis.Generators.Abstractions. Группа endpoints звонит MapEndpointsGenerated(), произведенный Tolitech.MinimalApis.Generators, а новые endpoints регистрируются с помощью сгенерированного кода.

Почему это имеет значение

  • Меньше ручной проводки: разработчикам не нужно вручную сопоставлять каждую endpoint.
  • Согласованность во время компиляции: endpoints имеют одну и ту же структуру и обнаруживаются сгенерированным кодом.
  • Чистый запуск: Program.cs делегирует настройку служб, промежуточного программного обеспечения и методов расширения.
  • АОТ-совместимость: уменьшает зависимость от отражения во время выполнения.
  • Согласованный OpenAPI: теги, сводки, коды состояния и версия генерируются вместе с endpoint.
  • Архитектурное выравнивание: HTTP находится в API, оркестровка осуществляется через MediatR, а контракты можно передавать клиентам.

Поток во время выполнения

  1. Program.cs создает приложение и вызывает расширение из endpoints службы или модуля.
  2. Расширение создает набор версий API и группу endpoints, применяет авторизацию/ограничение скорости и осуществляет вызовы. MapEndpointsGenerated().
  3. Сгенерированный картограф вызывает метод MapEndpoint каждой endpoint.
  4. Каждая endpoint сопоставляет маршрут, метаданные OpenAPI, разрешения, требования клиента и обработчик.
  5. Обработчик получает входные данные HTTP, преобразует их в команду/запрос, отправляет в MediatR и возвращает typed result.

Определения ошибок

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

lino error-definition new --name <ErrorDefinitionName> --service <ServiceName> --module <ModuleName> --entity <EntityName>
lino error-definition list --service <ServiceName> --module <ModuleName> --entity <EntityName>

В сгенерированных API ожидаемые ошибки, возвращаемые командами и запросами, должны быть преобразованы в ProblemDetails с кодом состояния, кодом ошибки и защищенным сообщением. Сервер может вести подробные технические журналы, но ответ HTTP должен оставаться последовательным, безопасным и локализуемым.

СтатусТипичное использованиеПример
400 неверный запросНеверный ввод или ошибка проверки.Отсутствует обязательное поле, неверный формат, нарушено правило запроса.
401 НесанкционированныйНеаутентифицированный пользователь.Токен отсутствует, просрочен или недействителен.
403 ЗапрещеноАутентифицированный пользователь без разрешения.Требуется разрешение RequirePermission не предоставлено.
404 Не найденНесуществующий ресурс или выход за пределы разрешенной области.ProductNotFound, объект от другого клиента или неизвестный идентификатор.
409 КонфликтГосударственный конфликт или правило уникальности.Дублирующаяся запись, неверный переход, конфликт версий.

Передовая практика

  • Создайте определения ошибок для ожидаемых и повторно используемых сбоев.
  • Не возвращайте клиенту технические исключения или сведения об инфраструктуре.
  • Поддерживайте стабильные коды ошибок, чтобы интерфейс, typed clients и тесты могли безопасно реагировать.
  • Задокументируйте в OpenAPI коды состояния, которые может создавать endpoint.
  • Используйте журналы на стороне сервера для получения диагностических сведений и защищенных сообщений в общедоступных ответах.
Произошла необработанная ошибка. Обновить 🗙