Задача звучит буднично: проверить, что заказ отдаётся по номеру. Запрос собран, кнопка нажата, в ответе 200 — проверка закрыта. А в теле лежало <soap:Fault>: заказа с таким номером нет, сервис честно об этом сказал, и статус к его словам отношения не имел.
Промахиваются тут не от невнимательности. У API есть стиль — договорённость, где адрес, где действие и где ошибка. Стилей три, и от стиля зависит, куда слать запрос, где искать поле и что считать отказом.
Один и тот же вопрос в трёх стилях: REST спрашивает по адресу заказа и отвечает статусом, SOAP шлёт конверт на общий адрес, GraphQL перечисляет нужные поля. В двух случаях из трёх отказ приезжает внутри тела с кодом 200 — статус говорит лишь то, что сервер ответил.
REST: адрес — это вещь, метод — действие
Самый частый стиль стоит на одной идее: у каждой вещи есть свой адрес, а что с ней сделать — говорит метод. Заказ 42 живёт по адресу /orders/42: получить — GET, создать — POST /orders, удалить — DELETE /orders/42. Действие в адрес не пишут: адрес отвечает на «что», метод — «что сделать». Отвечает сервер в JSON, а итог кладёт в статус-код: 200 — отдал, 201 — создал, 404 — такого заказа нет, 400 — тело не годится.
Второе свойство объясняет плавающие дефекты: сервер не помнит предыдущий запрос. Кто вы такой, сказано в каждом заголовком с токеном. Значит, запросы гоняют по одному и в любом порядке; если второй работает только сразу после первого, сервер что-то запомнил у себя.
«Недо-REST» узнают по трём признакам: действие уехало в адрес (/getOrder?id=42); всё, включая чтение, шлётся методом POST; и худшее — 200 с {"error": "order not found"} в теле. Последнее меняет работу: одного статуса мало, проверку ведут по телу. А если в описании обещаны 4xx, расхождение заводят как дефект.
SOAP: конверт, один адрес и отказ внутри успеха
SOAP старше REST и живёт там, где системы договорились давно и надолго: банки, страховые, государственные сервисы.
Всё сообщение — XML-конверт: Envelope, внутри Header со служебным (подпись, реквизиты) и Body с операцией и параметрами. Адрес один на весь сервис, метод почти всегда POST, а что делают — написано в конверте. Следствие: во вкладке «Сеть» по адресу не видно, что происходило, — открывать надо тело.
Описание сервиса — WSDL: XML-файл со списком операций, типами полей и адресом; его импортируют в Postman или SoapUI, и заготовки запросов создаются сами.
Главное для проверок — как приходит отказ: <soap:Fault> внутри тела.
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<soap:Fault>
<faultcode>soap:Client</faultcode>
<faultstring>Order 42 not found</faultstring>
</soap:Fault>
</soap:Body>
</soap:Envelope>
По спецификации фолт полагается отдавать с кодом 500, но на практике сервисы сплошь отвечают 200: деловой отказ — «нет такого заказа», «счёт заблокирован» — кладут в тело успешного конверта. В SOAP статус смотрят, а решают по телу.
GraphQL: поля заказывает клиент
Обратная беда REST: чтобы показать один экран, приложение дёргает три адреса, а из тридцати полей рисует два. GraphQL переворачивает порядок — адрес один (/graphql), а нужные поля клиент перечисляет прямо в теле запроса:
query {
order(id: 42) {
total
customer { name }
}
}
Ответ повторяет форму запроса: data.order.total, data.order.customer.name — ровно запрошенное. Значит, «тот же самый запрос» у двух проверок отличается набором полей, и зелёная первая ничего не обещает второй.
Ошибки лежат в массиве errors рядом с data, а статус при этом 200. Ответ бывает и частичным: data пришла, часть полей внутри null, причина — в errors.
Кроме значений проверяют ещё три вещи. Поле, которого нет в схеме: сервер обязан не выполнять запрос вовсе и ответить ошибкой, а не пропустить его молча. Глубину: цепочка заказ → покупатель → его заказы закольцовывается, и без ограничения сервис кладут одним запросом. Права на поле: телефон покупателя не должен приезжать тому, кому не положен, даже если ручка доступна.
Три стиля рядом
Шпаргалка на первый день:
| REST | SOAP | GraphQL | |
|---|---|---|---|
| Адрес | свой у ресурса | один на сервис | один: /graphql |
| Действие | метод GET, POST | операция в конверте | query, mutation |
| Где отказ | статус 4xx/5xx | <soap:Fault> при 200 | errors при 200 |
| Контракт | OpenAPI, Swagger UI | WSDL | схема сервиса |
| Чем смотреть | Postman | Postman, SoapUI | Postman, GraphiQL |
Где лежит контракт: OpenAPI и Swagger
«Поле называется customerId или clientId?», «а 404 здесь бывает?» — по памяти на такие вопросы не отвечают: ответ лежит в описании API.
Для REST описание пишут в формате OpenAPI: файл YAML или JSON с адресами, методами, параметрами, схемами тел, обязательными полями, допустимыми значениями и кодами ответов. Swagger — инструменты вокруг формата; обычно встречается Swagger UI: то же описание списком, с кнопкой отправки из браузера. Ищут по адресам вроде /swagger-ui, сам файл — /v3/api-docs.
Смотрят четыре вещи: список адресов — ручка, про которую в требованиях ни слова, уже находка; обязательные поля и типы — основа негативных проверок; перечисления значений — готовые классы эквивалентности; коды ответов с телами ошибок — ожидаемый результат.
Из того же файла собирается коллекция: в Postman «Import» → файл или ссылка, и запросы с примерами тел создаются сами; для SOAP так же импортируют WSDL. У GraphQL описание встроено в сервис — типы и поля он отдаёт по запросу-интроспекции.
Расхождение описания и поведения — дефект: по описанию соседние команды пишут клиентов и автотесты. Формулируют через обе стороны: «в OpenAPI поле phone обязательное, сервис принимает запрос без него и отвечает 201»; что чинить — код или описание — решает владелец сервиса.
Где спотыкаются
- Судят по статусу.
200значит «сервер ответил», а не «получилось»: в SOAP и GraphQL отказ приезжает в теле с тем же200, в неаккуратном REST — тоже. - Сверяют поля по памяти. «Кажется, было
customerId» — и проверка зелёная на поле, которого в ответе нет. Имена и типы берут из схемы. - Пропускают частичный ответ GraphQL.
dataне пустая, нужное поле —null, причина вerrors. - Верят устаревшему описанию. Swagger UI показывает вчерашний контракт, сервис отвечает иначе. Само расхождение — находка; кейсы под устаревшее описание не подгоняют.
Коротко
- Стиль API задаёт, где адрес, где действие и где ошибка; статус — половина проверки, вторая половина всегда в теле.
- REST: адрес у каждого ресурса свой, действие — метод, итог — в статус-коде; глагол в пути и
200с ошибкой в теле — отклонение. - SOAP: XML-конверт, один адрес, операция внутри; отказ приходит как
<soap:Fault>, часто при200. - GraphQL: один адрес, поля выбирает клиент; ошибки — в
errorsпри200, ответ бывает частичным. - Контракт берут из OpenAPI и Swagger UI, из WSDL или из схемы GraphQL; расхождение с поведением — дефект.
Что почитать дальше
- Клиент-сервер и HTTP — методы, статус-коды и заголовки под всеми стилями.
- API-тестирование в Postman — как собрать запрос руками и проверить ответ.
- Вход, сессии и токены — чем представляться сервису в любом стиле.
- Автотесты API на Python — те же проверки без рук.