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

Документация API приложения

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

lesson-30/after архива продолжения — самостоятельная ветка публичного чтения четырёх курсов. Она использует Microsoft.AspNetCore.OpenApi версии 10.0.12 вместе с net10.0; Identity и закрытые команды в неё не включены. Генератор, сборка, документ и сервер при подготовке не запускались. Приведённая структура OpenAPI ожидается по исходникам, а не выдаётся за уже выгруженный файл работающего API.

Генератор получает метаданные endpoint

Пакет указан явно в project file. До Build регистрируется AddOpenApi, после — MapOpenApi. Для Development документ доступен по /openapi/v1.json. Swagger UI и другой интерактивный frontend не устанавливаются: JSON-документ и интерфейс просмотра являются отдельными компонентами.

builder.Services.AddOpenApi();
var app = builder.Build();
app.MapOpenApi();
app.MapGet("/api/courses", () => TypedResults.Ok(CatalogData.All))
    .WithName("ListCourses")
    .WithSummary("Публичный список курсов")
    .WithDescription("Четыре учебных курса, без внутренних заметок.");

Метаданные имени помогают получить стабильный operation ID, summary объясняет назначение, а описание уточняет выбранную модель. Return type Ok<CourseResponse[]> показывает успешный JSON-массив. Если вернуть общий IResult без дополнительных сведений, генератор может знать меньше о конкретной форме ответа.

Однако слова «четыре курса» описывают учебные данные, а не вечное ограничение API. При расширении набора нужно обновить комментарий, чтобы документация не утверждала фиксированное количество объектов. Кодовая генерация не проверяет человеческий смысл описания.

Возможные ответы относятся к одному контракту

Деталь по /api/courses/{id} возвращает либо Ok<CourseResponse>, либо NotFound. В этой ветке это явный union typed results, а не скрытый выбор разных тел за общим типом. Генератор получает сведения о двух статусах и схеме успешного представления.

static Results<Ok<CourseResponse>, NotFound> Find(string id)
{
    var course = CatalogData.All.FirstOrDefault(c => c.Id == id);
    return course is null ? TypedResults.NotFound() : TypedResults.Ok(course);
}

Не следует копировать описание ошибки из раннего API с Problem Details, если новый handler на самом деле возвращает пустой NotFound. Документ должен отражать конкретную реализацию. Когда добавляется общая error boundary, нужно согласовать её ответы отдельно; генератор не обязательно выведет все middleware-ошибки из тела endpoint.

В архиве есть contract-table.md: методы и пути, параметры, успех, отсутствие, примеры ID. Он служит редакционной сверкой между кодом и документом. Таблица не заменяет фактическую генерацию; после разрешения исполнения понадобится прочитать настоящий JSON и сопоставить его с этой договорённостью.

Схема не выражает весь доменный смысл

Тип string для названия не объясняет сам по себе правило двух–ста двадцати Unicode-скаляров, trim и запрет нулевого символа из validation раннего проекта. Атрибуты и transformers могут добавлять часть ограничений, но нельзя автоматически приравнивать maxLength к каждой собственной реализации счёта символов без проверки семантики.

Точно так же integer не сообщает, что число уроков допустимо только 1–500, если метаданные не добавлены. В учебной ветке нет изменяющей команды, поэтому она не обещает описать весь validator CRUD. Контракт каждого endpoint должен перечислять только реально поддерживаемую форму, а не пожелания будущего интерфейса.

Примеры запросов должны быть согласованы с данными. javascript существует, missing-course отсутствует. Пример нового ID из POST не нужен, потому что POST здесь нет. Копирование большого общего документа с несуществующими endpoints выглядит полезно, но заставляет клиента строить интеграцию на выдуманном интерфейсе.

Документ безопасности не включает защиту

Security scheme в OpenAPI описывает, что клиенту понадобится credential. Она не вызывает RequireAuthorization, не выпускает cookie и не проверяет CSRF. Если потом добавить закрытые команды, необходимо одновременно настроить реальные middleware и политику, затем описать их контракт. Иконка замка в интерфейсе документации не является доказательством отказа анонимному запросу.

Для cookie-команд особенно важно дополнительно объяснить получение CSRF token и его обновление после смены личности. Такой workflow не сводится к полю «cookie name» в security scheme. Нельзя предлагать пользователю вставлять секретные production-cookies в общедоступный интерактивный инструмент ради удобной демонстрации.

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

Изменение версии начинается с наблюдаемого отличия

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

Для будущего самостоятельного исполнения прочитайте /openapi/v1.json и найдите оба пути. Проверьте JSON-массив списка, схему детали, параметр ID и 404. Затем отправьте запрос существующего и отсутствующего ID и сравните реальное тело с документом. Сейчас такие обращения не выполнены, поэтому не следует добавлять отметку «контракт проверен» только по наличию AddOpenApi.

Урок даёт связанную пару кода и описания для выбранного маленького API. Следующий шаг — определить, какие проверки действительно способны заметить расхождение на уровне домена, базы и HTTP. Подключение и генерация описаны в официальном руководстве OpenAPI ASP.NET Core 10, версии пакета — на NuGet.

Operation ID стоит сохранять при совместимом изменении внутренней реализации, потому что генераторы клиентов могут использовать его для имён методов. Но одинаковый operation ID не оправдывает несовместимую схему ответа. Таблица контракта отдельно фиксирует тип массива и четыре публичных поля, чтобы при рефакторинге не появилась внутренняя заметка. Также она отмечает отсутствие POST, PUT и DELETE: клиент не должен предполагать эти методы из общего имени курса. Для документа полезны понятные примеры, однако каждый пример должен иметь обозначенный источник и состояние. Если результат получен только чтением исходников, это редакционное ожидание; после реальной генерации понадобится зафиксировать документ и сравнить его содержимое с HTTP.