← назад к разделу

Задача звучит буднично: проверить, что заказ отдаётся по номеру. Запрос собран, кнопка нажата, в ответе 200 — проверка закрыта. А в теле лежало <soap:Fault>: заказа с таким номером нет, сервис честно об этом сказал, и статус к его словам отношения не имел.

Промахиваются тут не от невнимательности. У API есть стиль — договорённость, где адрес, где действие и где ошибка. Стилей три, и от стиля зависит, куда слать запрос, где искать поле и что считать отказом.

один вопрос — «дай заказ 42» — в трёх стилях REST · GET /orders/42адрес — сам заказ200тело: заказнет заказа → 404 SOAP · POST /serviceоперация внутри конверта200<soap:Fault>отказ внутри тела GraphQL · POST /graphql{ order(id:42){ total } }200"errors": [ … ]data частично null статус 200 здесь говорит только «сервер ответил»получилось ли — написано в теле

Один и тот же вопрос в трёх стилях: 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.

Кроме значений проверяют ещё три вещи. Поле, которого нет в схеме: сервер обязан не выполнять запрос вовсе и ответить ошибкой, а не пропустить его молча. Глубину: цепочка заказ → покупатель → его заказы закольцовывается, и без ограничения сервис кладут одним запросом. Права на поле: телефон покупателя не должен приезжать тому, кому не положен, даже если ручка доступна.

Три стиля рядом

Шпаргалка на первый день:

RESTSOAPGraphQL
Адрессвой у ресурсаодин на сервисодин: /graphql
Действиеметод GET, POSTоперация в конвертеquery, mutation
Где отказстатус 4xx/5xx<soap:Fault> при 200errors при 200
КонтрактOpenAPI, Swagger UIWSDLсхема сервиса
Чем смотретьPostmanPostman, SoapUIPostman, 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; расхождение с поведением — дефект.

Что почитать дальше