В этом репозитории находится пример спецификации (папка example) и описаны правила работы с ней, а также распространенные кейсы (файл cases).
- Лучшии практики ведения API спецификации (OpenAPI) в Surf
- Содержание
- Общие принципы
- Организация файлов и папок
- Проектирование спецификации API
- Инструменты
- Генерация папок для фичи
- Базовая структура
- Ресурсы и методы
- Структура и проектирование моделей
- Быстрые и полезные комбинации
- Проверка спецификации API на фичу
- Проверка всей спецификации API через линтер SurfGen
- Дополнительная информация для ознакомления и советы
- Спецификация должна быть побита на разные файлы. Это необходимо для того, чтобы ее можно было легко и приятно поддерживать (дополнять, изменять) и вообще читать.
- Корневой README.md должен содержать информацию о том, как работать с источниками в репозитории. Потому что кто-то может быть не в курсе.
- Хорошо иметь скрипт для автогенерации шаблона. Чтоб не копипастить каждый раз самим.
- Старайтесь использовать текстовый формат (не используйте GUI), потому что так проще навигировать. И так можно пользоваться поиском.
- Относитесь к спецификации как к программному коду. Иначе в конечном итоге она превратится в мусор.
Хорошо придерживаться следующего паттерна:
- root_folder <- корень вашего репозитория
-- README.md
-- feature_or_api_controller <- папка, имеющая название фичи
--- api.yaml <- Файл исключительно для описание сервисов (path в Swagger)
--- models.yaml <- Файл исключительно для описания моделей (components/schemas в Swagger)
--- parameters.yaml <- Файл исключительно для описания параметров api-методов (components/parameters в Swagger)
--- errors.yaml <- Файл исключительно для описания ошибок которые могут вернуть методы (components/schemas в Swagger)
ВАЖНО
api.yaml
иmodels.yaml
обязательны! Потому что в противном случае люди будут бояться лезть менять ваши файлы.parameters.yaml
иerrors.yaml
не обязательны! Они могут быть использованы в тех случаях, когда сервисы содержат очень много повторяющихся параметров и/или ошибок.- Не стоит выносить отдельно components/requestBodies , потому что тогда, чтобы добраться от метода до модели, потребуется 2 перехода. Это уже неудобно.
Перед проектированием спецификации API подразумевается, что вы:
- Клонировали репозиторий и наполнили его файлами для генерации и readme.
- Создали ветку фичи, которую будете проектировать.
- Установили необходимые расширения в VSCode.
- VSCode скачать и установить, если еще не установлен.
- Расширения для VSCode - для установки переходим в раздел Расширения в левом боковом меню и ищем по названию необходимое расширение:
- Swagger Viewer - Предварительный просмотр спецификации API, происходит в режиме реального времени по мере ввода.
- GitHub Pull Requests and Issues (опционально) - можно смотреть и комментировать PR-ы прямо из VSCode. Необходимо авторизоваться через VSCode в GitHub, появится иконка GitHub-а в боковом меню, где можно просматривать и комментировать PR-ы.
- Git Blame (опционально). Расширение Git Blame предоставляет возможность просматривать информацию состояния для текущей выбранной строки. Оно позволяет выяснить, кто писал определенные фрагменты кода.
- Git History (опционально). Расширение Git History предоставляет возможность тщательно изучить историю файла, автора, ветки. Чтобы активировать окно Git History, нужно кликнуть на файл правой кнопкой мыши и выбрать Git: View File History. Кроме того, вы можете сравнивать ветки и коммиты, создавать ветки из коммитов и многое другое.
Хорошо иметь какой-то инструмент, который позволит сгенерить нужные файлы с предзаполненной мета информацией - чтоб люди не писали по 100 раз одно и то же.
В этом репозитории это сделано с помощью шелл-скрипта gen.sh и двух файлов с шаблонами .api_template и .models_template
Сам скрипт позволяет сгенерировать "контроллер" с отдельным файлом для моделек и отдельным файлом для методов API. Т.к. мы проектируем уже фичу, то все махинации должны производиться в соответствующей ветке.
Пошаговая инструкция генерации папки для фичи:
№ | Шаг |
---|---|
1 | Проверяем, что мы в правильной ветке. Если работаем над авторизацией, то в ветке auth, которую мы создали. |
2 | Открываем консоль, перемещаемся в корень репозитория: 1. Идем в папку где располагается репозиторий (к примеру, E:\work\my-project-swagger ). 2. Shift+клик по правой кнопка мыши → “Открыть окно PowerShell здесь”, на macOS соответственно терминал открываем или же через терминал VSCode. |
3 | Вводим в консоль ./gen.sh ./{название фичи (оно же название папки)} . Пример: ./gen.sh ./auth или sh gen.sh catalog |
4 | Скрипт создаст папку catalog и добавит в нее два файла api.yaml и models.yaml с уже заполненной шапкой. Если папка уже есть, то скрипт просто положит в нее два файла. Всё, папки появились. Если нет, жмем кнопку обновления проводника (Explorer) в VSCode. Созданы два файла в папке auth с нужными шапками/шаблонами: api.yaml - для описания эндпоинтов, models.yaml - для описания моделей к ним |
5 | Можем начинать проектирование фичи, для которой создали папку. |
Структура файла api.yaml
или что нагенерил мне скрипт:
Все поля, кроме
path
, заполняются при генерации папок. Но понимать, что они означают будет полезно.
Название поля | Описание |
---|---|
openapi |
Обязательное для заполнения. Номер версии спецификации OpenAPI. Последняя версия на данный момент 3.0.2. Это поле не связано со строкой API - info.version . |
info |
Обязательное для заполнения. Содержит основную информацию о вашем API: название ( title ), описание вашего API (description ), версия вашего API (version ), контакты разработчика спецификации (contact.name , contact.email ). В данной инструкции указан не весь набор элементов, которые могут содержаться в info-блоке, но для нашего использования этого достаточно. Пример: info:
title: "API"
version: "1.0.0"
contact:
name: Юльская Виктория
email: yulskaya@surfstudio.ru |
servers |
Содержит информацию об используемых серверах: url и описание сервера (description). Их может быть несколько. Пример: servers:
- url: https://dev3.myproject.ru/api/surf/v1/
description: Test server dev3
- url: https://surf.myproject.ru/api/surf/v1/
description: Test server surf
- url: https://myproject.ru/api/surf/v1/
description: Production Server |
components |
В объекте components можно хранить множество различных переиспользуемых объектов. Объект components может содержать следующее: При делении папки на два файла api.yaml и models.yaml нам данный блок нужен только для определения схемы безопасности, все остальное уходит в файл models.yaml. Лучше сразу понять как будет проходить авторизация и дальнейшая проверка наличия прав пользователя при запросе на ресурс - через токены / куки / что-то другое (разработчикам надо закладывать это в самом начале, во время первого спринта, а то и во время инициализации). Пример: components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT |
security |
Для отправки запросов, авторизованных нашим сервером API, спецификация должна содержать информацию о безопасности, которая авторизует запрос. security:
- bearerAuth: [] Объявленные поля components.securitySchemes и security свидетельствуют о том, что у любого метода в этом файле должен быть установлен хедер Authorization с JWT токеном. |
paths |
Обязательное для заполнения. Содержит доступные пути (конечные точки) и операции (методы) для API. Подробнее о заполнении данного блока рассмотрим чуть ниже. |
Блок path состоит из:
- пути (конечной точки) - все пути в блоке
path
задаются относительно URL, определенных в блоке "Серверы”, то есть полный URL запроса будет выглядеть так<server-url>/path
. - операций (методов
GET
,POST
и тд), который в свою очередь включает:summary
- название метода.description
- описание работы метода. Описывайте там задачу которую решает метод или свойство.security: []
- указывается если для запроса НЕ нужна авторизация.parameters
- параметры запросаrequestBody
- тело запросаresponses
- описание ответа
Есть и другие элементы, которыми нам особо не нужно пользоваться пока что.
Есть 3 типа ресурсов:
- Документ - один объект. К примеру, одно сообщение в списке (
api/messages/{id}
- документ обычно вложен в коллекцию, но есть исключения).- в пути используются в таком случае только существительные.
- последнее существительное в единственном числе.
- Коллекция - множество объектов. К примеру, список сообщений (
api/messages
).- в пути используются в таком случае только существительные.
- последнее существительное во множественном числе.
- Контроллер - действие. К примеру, оформление заказа (
api/cart/checkout
).- можно использовать глаголы.
- последнее слово всегда глагол.
- действие всегда должно относится к чему то (
api/cart/checkout
- checkout относится к корзине, не может быть простоapi/checkout
).
Стараемся делать как можно больше документов и коллекций, и как можно меньше контроллеров
В названии пути НЕ ПИШЕМ ДЕЙСТВИЕ, о котором говорит HTTP method!!! (create, update, delete и тд)
- POST /courses - Создать новый курс
- POST /courses/create - Создать новый курс
Параметры пути и запрос состоят из:
name
: имя параметра.in
: место параметра. Возможные значения:header
- параметры, включенные в заголовок запроса, обычно относятся к авторизации.path
- параметры в пределах path конечной точки перед строкой запроса. Обычно эти параметры выделяются фигурными скобками.query
- параметры в строке запроса конечной точки, располагаются после знака ?.cookie
- параметры в заголовке Cookie.
description
: описание параметра.required
: требуется ли параметр.schema
: схема или модель для параметра. Схема определяет структуру входных или выходных данных.example
: пример типа носителя. Если объект example содержит примеры, эти примеры появляются в Swagger UI, а не в содержимом объекта example.
Также, параметры запроса можно выносить в models.yaml
и ссылаться ($ref
) на параметры из моделей. Пример:
parameters:
- $ref: "models.yaml#/components/parameters/Param1"
Для добавления в компоненты параметров необходимо на уровне с элементом schema
добавить элемент parameters и описать там все необходимые параметры.
Для ограничения возможных значений параметра запроса необходимо использовать ключевое слово enum
.
# Описание параметра запроса в models.yaml для того, чтобы ссылаться на него и переиспользовать без дублирующего описания.
parameters:
filter_type:
name: filter_type
in: query
description: |
Тип фильтра заказов пользователя:
- all - все заказы
- current - текущие
- done - выполненные
schema:
enum: ["all", "current", "done"]
type: string
Для того чтобы добавить параметр в путь запроса необходимо использовать фигурный скобки {}
. Обычно это используется для указания определенного элемента в коллекции. Путь может иметь несколько параметров:
GET /users/{id}:
GET /cars/{carId}/drivers/{driverId}:
Каждый параметр пути должен быть заменен фактическим значением при вызове.
Для определения параметров пути нужно использовать следующую конструкцию in: path
. Необходимо также добавить required: true
, чтобы указать обязательность данного параметра.
paths:
/users/{id}:
get:
parameters:
- name: id # имя можно использовать такое же как и в пути
in: path
description: Идентификатор пользователя
required: true # обязательный параметр
schema:
type: integer
minimum: 1
Параметры запроса отображаются в конце URL-адреса после знака вопроса (?
). Несколько значений должны разделяться амперсандом (&
).
GET /pets/findByStatus?status=available
GET /notes?offset=100&limit=50
Для определения таких параметров нужно использовать следующую конструкцию in: query.
paths:
/notes:
get:
parameters:
- name: offset
in: query
description: The number of items to skip before starting to collect the result set
schema:
type: integer
- name: limit
in: query
description: The numbers of items to return
schema:
type: integer
Не декларируйте здесь объекты!!!
Примеры оформления параметров запроса
Про параметры заголовка и куки подробнее можно прочитать в соответствующих разделах.
POST, PUT и PATCH запросы могут иметь тело запроса.
1. Мы всегда должны ставить ссылки на модели!!! Не нужно засорять нашу спецификацию перечислением того, что должно быть в моделях. У нас и так огромные методы, а если еще модели писать, то будет много дублирования и иных проблем. Делаем ссылки для своего удобства и для удобства всей команды.
2. Есть исключения в виде массивов или групп, в таком случае мы прописываем массив и как тип элементов, которые там лежат мы используем ссылку на модель элемента.
Пример:
requestBody:
required: true
content:
application/json:
schema:
$ref: "models.yaml#/components/schemas/RecalculateOrderRequest"
Описание REST-запроса обязательно должно содержать описание ответа (responses
). У каждого метода должен быть определен хотя бы один ответ (успешный). Response задается HTTP-кодом ответа и данными, которые возвращаются в теле ответа и / или заголовке.
1. Мы всегда должны ставить ссылки на модели!!! Не нужно засорять нашу спецификацию перечислением того, что должно быть в моделях. У нас и так огромные методы, а если еще модели писать, то будет много дублирования и иных проблем. Делаем ссылки для своего удобства и для удобства всей команды.
2. Есть исключения в виде массивов или групп, в таком случае мы прописываем массив и как тип элементов, которые там лежат мы используем ссылку на модель элемента.
Описание ответа начинается с кода, такого как 200
или 404
. Методы обычно возвращают один успешный код и один и более кодов ошибок. Каждый код требуется описания (description
) - условие, при которых код срабатывает. Если вы не можете задать определенный код, то его можно задать следующим видом: 1XX
, 2XX
, 3XX
, 4XX
, 5XX
. Но таким образом, в случае если был задан код 404
и 4XX
, приоритет у первого будет выше.
responses:
'201':
description: Бонусы успешно списаны.
'500':
description: |
Возможные ошибки
* `101` - UserBlocked, пользователь был заблокирован
* `104` - OTPCodeInvalid, неверный OTP-код
content:
application/json:
schema:
$ref: "../common/models.yaml#/components/schemas/ErrorResponse"
'426':
description: Необходимо обновить приложение
Не стоит описывать все возможные коды ответов, тем более что о некоторых из них можно и не знать в момент проектирования запроса. Важно при описании кодов покрыть случай успешного выполнения запроса и коды ошибок, известных на момент написания REST-запроса.
Также, в Surf обычно описываются кастомные ошибки и заворачиваются в400
или500
статус код.
Кастомные ошибки описываются в отдельной папке common - где вapi.yaml
описывается справочник ошибок, а вmodels.yaml
описывается модель ошибки, к примеру,ErrorResponse
, который состоит из кода кастомной ошибки, сообщения в человекочитаемом виде, и при необходимости вспомогательная информация об ошибке
Для передачи файлов в запросе или ответе в OpenAPI 3.0 используется type: string и format: binary или format: base64.
paths:
/report:
get:
summary: Returns the report in the PDF format
responses:
'200':
description: A PDF file
content:
application/pdf:
schema:
type: string
format: binary
Статус коды
Код | Описание | Часто используемые коды |
---|---|---|
2xx | Операция завершилась успешно | 200 - Все ок! Всегда содержит тело ответа. Может использоваться в GET запросах. 201 - Запись создана. Используется в методах POST и имеет тело ответа, чтобы сказать клиенту, что мы создали в итоге - как минимум получить идентификатор записи). 202 - Принято. Не содержит тело ответа. Говорит о том, что клиенту не обязательно ждать завершения операции (но она еще не завершилась). Пример, оплата. 204 - Операция прошла успешно, но ответ пустой. Если знаем что мы не ждем ответа, то ставим всегда данный код. |
3xx | Редирект или можем пойти читать из кэша | 304 - Данные не изменились. Можно читать данные из кеша. Обычно работает с E-Tag или Cache-Control заголовками. Работает только с GET запросами |
4xx | Операция завершилась с ошибкой по вине клиента | Из тех, что стоит фиксировать в спецификации: 401 - Пользователь не авторизован для доступа. 403 - Пользователь не имеет права просматривать контент. 426 - Указывает, что сервер отказывается выполнять запрос с использованием текущего протокола, но может захотеть сделать это после того, как клиент обновится до другого протокола (используется когда версия приложения уже не поддерживается и пользователю предлагается обновить приложение при получении данной ошибки). Также, на 400 или 409 можно повесить кастомные ошибки и описать их в справочнике |
5xx | Операция завершилась с ошибкой по вине сервера (или не смог сразу определить что по вине клиента) | Конкретные 5xx ошибки не фиксируем обычно в спецификации API, но если необходима необычная обработка, то фиксируйте (к примеру определенная заглушка на ошибку временной неработоспособности сервера - 503 ) |
Файл models.yaml состоит из
components
, который в свою очередь включает:schemas
- моделиparameters
- параметры
Пример структуры файла:
components:
schemas:
UpdatedOrderResponse:
type: object
description: Модель для обновленных полей заказа после выполнения действия над ним.
properties:
status:
$ref: "#/components/schemas/ExtendedOrderStatus"
actions:
type: array
description: |
Список действий, доступных над заказом.
Список пуст, если нет доступных действий.
items:
$ref: "#/components/schemas/OrderAction"
required:
- status
- actions
ReceiverType: # модель, которая содержит ограничения возможных значений по типу плательщика
type: string
enum: [individual, entity]
description: |
Тип плательщика:
- individual - физическое лицо
- entity - юридическое лицо
parameters: # параметры, которые можно переиспользовать в параметрах запроса
filter_type:
name: filter_type
in: query
description: |
Тип фильтра заказов пользователя:
- all - все заказы
- current - текущие
- done - выполненные
schema:
enum: ["all", "current", "done"]
type: string
Комментарий (
description
) очень важная часть спецификации. Применимо как к методам, так и к моделям.
Уделяйте большое внимание этому полю и описывайте как можно более понятнее, вкладывайте контекст, логику, примеры - пишем как можно больше (в пределах разумного, конечно, описывать супер подробноuser.name
не стоит).
С помощь ключевого слова type задается тип данных. Типы могут быть следующими:
string
- Строка текста.number
- включает в себя и целые числа, и числа с плавающей точкой.integer
- только целые числа.boolean
- в логическом типе boolean представлено два возможных значения:true
иfalse
.array
- массив.object
- объекты - коллекция пар элемент и значение.
Строка
- Длину строки можно ограничить, используя для этого minLength и maxLength.
- Ключевое слово
pattern
позволяет определить шаблон регулярного выражения для строки - значения, которые могут быть использованы в строке. Для заданияpattern
используется синтаксис регулярного выражения из JavaScript (pattern: '^\d{3}-\d{2}-\d{4}$'
)."^"
используется для обозначения начала строки,"$"
- конца строки. Без^… $
шаблон соответствует любой строке, содержащей указанное регулярное выражение. - Ключевое слово
format
используется для того чтобы задать формат строки, например один из них:date
(2017-07-21),date-time
(2017-07-21T17:32:28Z),password
,byte
,binary
К примеру, для передачи файла используется:
avatar: # изображение, встроенное в JSON
description: Base64-encoded contents of the avatar
type: string
format: byte
Числа
- Чтобы указать диапазон возможных значений, можно использовать ключевая слова
minimum
иmaximum
(minimum ≤value≤ maximum). - Чтобы исключить граничные значения, укажите
exclusiveMinimum: true
иexclusiveMaximum: true
count:
description: Суммарное количество товаров в заказе
type: integer
example: 6
maximum: 25
Массивы
- С помощью
minItems
иmaxItems
можно задавать минимальную и максимальную длину массива. Если не использоватьminItems
, то массив может быть пустым. - Элементы массива описываем отдельным элементом, если они представляют собой коллекцию.
# Элементы массива отдельным элементом
actions:
description: |
Список действий, доступных над заказом.
Список пуст, если нет доступных действий.
type: array
items:
$ref: "#/components/schemas/OrderAction"
# Массив строк
categories:
description: |
id категорий товаров первого уровня, в которые входят товары данной акции
type: array
items:
type: string
Объекты
- По умолчанию все элементы коллекции необязательные. Можно указать список обязательных элементов с помощью слова
required
(можно отказаться отrequired
и прийти к тому, чтобы явно прописывать параметрамnullable: false
, если поле не может быть пустым). - Коллекция также может быть вложенной и включать в себя коллекцию. В таком случае коллекцию оформляем отдельным объектом и даем на него ссылку для удобства всех членов команды.
Для описания данной информации следует создать папку common, где фиксировать общие договоренности, которые в самих методах никак не отразить. Также, можно выносить общие методы, к примеру, загрузка изображения (профиля, отзыва и тд).
Пример оформления файла common/api.yaml
openapi: 3.0.2
info:
title: "API"
version: "1.0.0"
contact:
name: Виктория Юльская
email: yulskaya@surfstudio.ru
description: |
# Headers
<details>
Для определения текущего пользователя, во всех запросах может как приходить, так и передаваться кастомный хедер `UserID`.
</details>
# Авторизация
<details>
Схема авторизации онована на механизме access/refresh токенов.
При логине - сервер возвращает пару access/refresh токен.
В дальнейшей работе с сервером во всех запросах необходимо передавать в хедере `Authorization`
полученный access токен.
При его протухании - необходимо произвести обновление токенов с помощью соответствующего метода,
используя refresh токен.
При получении ошибки во время обновления токенов - необходимо закончить текущую сесию работы с сервером
и разлогинить пользователя.
</details>
# Пагинация
<details>
Проект поддерживает страничную пагинацию.
Каждый пагинируемый запрос должен url-параметрами принимать `page` и `size`,
а в теле ответа возвращать данные с информацией о порции пагинируемых данных.
- `page` отвечает за размер пачки пагинации,
- `size` отвечает за размер порции.
</details>
# Ошибки
<details>
<summary>Описание</summary>
Сервер возвращает ответ со статус кодом 400 и ошибкой в формате:
{
"code": 100,
"errorMessage": "Пользователь не найден",
"data": "85"
}
Ошибки состоят из
- специфического кода `code` для идентификации ошибки
- текста `errorMessage`, который будет отображаться на клиенте
(в силу этого вся обязанность за формирование текста ошибок ложиться на серверную часть,
что позволит обеспечить гибкость механики в целом)
- и опционального поля `data`, в котором может располагаться строка с какими-то данными,
которые клиенту следует отобразить
(Например, это может быть количество секунд, через которые можно повторно отправить смс)
</details>
<details>
<summary>Специфические коды ошибок</summary>
* `100` - BadJson, неверный формат JSON от клиента
* `103` - OTPCodeRequestTooOften, слишком частые запросы на генерацию OTP-кода, в **data** - количество секунд до разблокировки
* `104` - OTPCodeInvalid, неверный OTP-код
* `105` - SendingCodeFailed, не удалось послать OTP-код
* `106` - FileUploadingError - Ошибка при загрузке файла
* `107` - NotEnoughPoints - Недостаточно баллов для списания
* `108` - NotEnoughProducts - Товар закончился
* `120` - NameNotUnique - Товар с таким именем уже существует
* `119` - RefreshTokenFailed - Не удалось обновить токен
* `121` - OTPCodeExpired - срок жизни кода истек
* `122` - BonusesWriteoffFailed - Нельзя списать больше бонусов, чем есть у пользователя
servers:
- url: https://someaddress.com
description: TODO заглушка, требуется поправить в будущем на реальный адрес.
components:
securitySchemes:
bearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- bearerAuth: []
paths:
/file:
post:
summary: Загрузка файла
description: Запрос на загрузку файла на сервер, используется, когда пользователь прикрепляет файлы
requestBody:
required: true
content:
multipart/form-data:
schema:
$ref: "models.yaml#/components/schemas/BinaryFile"
responses:
'200':
description: Успешный ответ с данными.
content:
application/json:
schema:
$ref: "models.yaml#/components/schemas/FileURL"
'400':
description: |
Возможные ошибки
* `106` - FileUploadingError - Ошибка при загрузке файла
content:
application/json:
schema:
$ref: "../common/models.yaml#/components/schemas/ErrorResponse"
/file/{fileUrl}:
delete:
summary: Удаление файла
parameters:
- name: fileUrl
in: path
description: Ссылка на файл
required: true
schema:
type: string
responses:
'204':
description: Файл удален
- Ctrl (или CMD) - Можем посмотреть что за ref у нас в методе / моделе. Наводим на ref и зажимаем комбинацию, получаем информацию о нашей ссылке. Если кликнуть по ней, то быстро перейдем по ссылке к нашей модели, на которую ссылаемся.
- Ctrl (или CMD) + Shift + O - Позволяет найти и перейти к конкретной вкладке, либо конкретной модели / свойству.
- Ctrl (или CMD) + Shift + P - Открывает панель команд.
- Ctrl (или CMD) + P - Поиск файлов по имени
Минимальная проверка спецификации API на фичу может быть проведена путем визуализации спецификации API при помощи комбинации Alt+Shift+P
. Комбинацию вызывать находясь в файле api.yaml
.
Проверить что спецификация визуализируется, все параметры отверстаны, прописаны обязательные и nullable
поля. Запросы и ответы также отрендерены и не отображаются ошибки.
Если не рендерится сваггер, на что обратить внимание:
Табуляция. Проверьте, что все находится на своем уровне
Ссылки. Проверьте, что все ссылки корректные и они ссылаются на существующие модели.
Проблема с табуляцией | Все ок |
---|---|
Для проверки должен быть подключен SurfGen
и подключен линтинг через GitHub Actions
.
Для корректной работы ветки называть feature_branchname
, к примеру, чтобы у всех веток (кроме мастера) был единый префикс.
Далее при открытии PR-а, при пуше изменений и тд будет запускать проверка спеки на соответствие всего вышеописанного.
- Если проставилась зеленая галка, то все с вашей спекой ок.
- Если видим красный индикатор, то есть проблемы и их нужно исправить.
Идем на вкладку Checks и читаем логи (build). Начинаем читать все, начиная с третьей строки. Что мы видим?
- Линтер пошел парсить схему
/common/models.yaml
- Зашел в объект
ErrorResponse
- В параметре
data
нашел ошибку- Выдает нам описание ошибки.
Чтобы исправить ошибку мы идем в папку common -> файл models.yaml -> находим модель ErrorResponse
, а в ней параметр data
. Исправляем описанную линтером ошибку.
После того, как мы запушим изменения с исправлением линтер снова запустится и продолжит проверку. Исправляем до тех пор, пока не получим зеленую галку)
Основные дефолтные хэдеры:
Accept-Charset
- способ клиента сказать в какой кодировке нужны данные (UTF-8
,ASCII
,whatever
). Обычно всегда используетсяUTF-8
и менять не нужно.Accept-Encoding
(аналог с сервера -Content-Encoding
) - то, как данные от сервера закодированы, обычно речь про алгоритм сжатия. Например,gzip
.Accept-Language
(аналог с сервера -Content-Language
) - то, какой язык хочет получить клиент. Использовать можно для мультиязычных сервисах.Accept
(аналог с сервера -Content-Type
) - Формат данных которые клиент поддерживает, эти форматы называются MIME-типами. Например,application/json
. Такое часто бывает при передаче файлов или когда хотим открыть файл в вебе, здесь нужно правильно установить MIME-тип.Cookies
- это способ хранить состояние. Как это работает:- Сначала сервер просит клиента установить
cookies
(Set-Cookie
). - Клиент их отправляет серверу при обращении в хэдерах с ключом
Cookie
.
- Сначала сервер просит клиента установить
Cookies
могут использоваться для передачи токена. Не самый лучший способ, но такое может быть.
В таком случае обязательные параметры для такихcookies
:
secure=true
httponly=true
samesite=strict
Совет | Описание |
---|---|
Используйте kebab-case для URL | Вот пример для списка заказов.
Плохо: /systemOrders или /system_orders
Хорошо: /system-orders
|
Используйте camelCase для параметров | Вот пример получения списка продуктов в магазине.
Плохо: /system-orders/{order_id} или /system-orders/{OrderId}
Хорошо: /system-orders/{orderId}
|
Используйте множественное число для коллекций | Если вы хотите получить всех пользователей.
Плохо: GET /user или GET /User
Хорошо: GET /users
|
Не используйте глаголы в URL ресурсов | Вместо этого пользуйтесь HTTP методами для описания операций.
Плохо: POST /updateuser/{userId} или GET /getusers
Хорошо: PUT /user/{userId}
|
Пользуйтесь глаголами в URL операций | Например, если вы хотите переслать уведомление пользователю.
Хорошо: POST /alerts/245743/resend
Помните, что resend не является [CRUD](https://ru.wikipedia.org/wiki/CRUD) операцией. Наоборот, это функция, которая выполняет определённое действие на сервере. |
Используйте camelCase для JSON свойств | Вместо этого пользуйтесь HTTP методами для описания операций.
Плохо: {
user_name: "Ванька Петров",
user_id: "1"
} Хорошо: {
userName: "Ванька Петров",
userId: "1"
} |
Используйте простой порядковый номер для версий | И всегда указывайте его на самом верхнем уровне.
Хорошо: http://api.domain.com/v1/shops/3/products
|
Указывайте количество ресурсов в ответе на запрос | Это свойство можно назвать total.
Плохо: {
users: [
...
], offset: 0
} Хорошо: {
users: [
...
] offset: 0,
total: 34
} |
Используйте параметры limit и offset |
И всегда указывайте его на самом верхнем уровне.
Хорошо: GET /shops?offset=5&limit=5
Потому что на фронтенде часто требуется пагинация |
Не передавайте аутентификационные токены в URL | И всегда указывайте его на самом верхнем уровне.
Это очень плохая практика, потому что часто URL логгируются, и токен также сохранится. Плохо: GET /shops/123?token=some_kind_of_authenticaiton_token
Хорошо: Вместо этого пользуйтесь заголовками. Authorization: Bearer xxxxxx, Extra yyyyy
Помните — время жизни токена нужно ограничивать |
Используйте HTTP методы для CRUD операций | В этом и есть их смысл.
GET : получение данных о ресурсах.
POST : создание новых ресурсов и подресурсов.
PUT : обновление существующих ресурсов.
PATCH : обновляет только определённые поля существующих ресурсов.
DELETE : удаляет ресурсы.
|
URL должен отражать структуру вложенных ресурсов | Примеры:
GET /shops/2/products : получить список продуктов из магазина 2.
GET /shops/2/products/31 : получить детали продукта 31 из магазина 2.
DELETE /shops/2/products/31 : удалить продукт 31 из магазина 2.
PUT /shops/2/products/31 : обновить данные о продукте 31. Используйте PUT на URL ресурса, а не коллекции.
POST /shops : создать новый магазин и вернуть данные о нём. Используйте POST на URL коллекции.
|