Comments 64
On Nov. 5, 2015, SmartBear in conjunction with 3Scale, Apigee, Capital One, Google, IBM, Intuit, Microsoft, PayPal, and Restlet announced the formation of the Open API Initiative, an open source project under the Linux Foundation. As part of the formation of the OAI, SmartBear donated the Swagger specification to the Linux Foundation, meaning that the OpenAPI Specification is semantically identical to the specification formerly known as the Swagger 2.0 specification.
Мне кажется немного запутанно получилось со Swagger и OpenAPI. По всей видимости имелась ввиду сама спецификация Swagger Specification, которая сейчас действительно называется OpenAPI Specification
Наверное, автор слишком уж "в теме" и не отдает себе отчет, что для новичков в этом вопросе не очевидно:
Изначально это был Swagger
но уже 2 года как переименован в OpenAPI.
Тем не менее осталось куча упоминаний о старом названии (например, в названии инструментов, например, существуют и сайт Swagger и сайт OpenAPIs)
Помните о том, что HTTP-протокол осуществляет транспортную функцию по доставке данных в запросах и ответах.
Скорее "чаще всего в вашем приложении HTTP-протокол осуществляет лишь транспортную функцию по доставке данных в запросах и ответах, игнорируя его семантику сверх практически обусловленного для доставки". И в этом случае как раз логично придерживаться пары GET/POST, забыть о существовании десятков кодов и т. п., а, главное, передавать в теле запроса не только представление ресурсов, но и их идентификаторы и действия над ними.
Делали ли вы генерацию кода по swagger спецификации, и делали ли автоматический рефакторинг при изменениях в спецификации?
Многие статьи о REST ссылаются на работу Roy Thomas Fielding «Architectural Styles and the Design of Network-based Software Architectures» как первоисточник определения REST (смотрите пятую и шестую главы этой работы). Рекомендаций использовать http- глаголы GET-POST-PUT-DELETE как единственный способ определения операций вы там не найдете.
Очень верное замечание. Есть REST — без которого интернет сейчас бы не был интернетом. И есть RESTAPI в примитивной трактовке. RESTAPI (CRUD/G-P-P-D) первоначально поражает своей идеей. Потом начинаются вопросы. Если объект имеет связи (а это общий случай) — как понять, когда включать связанне объекты, а когда не включать. До какого уровня «разворачивать» связанные объекты. Какие поля включать в ответ. Или если сказать проще, у RESTAPI нет необходимого разнообразия
Моя мечта начать использовать graphql, который тоже REST, но не RESTAPI. С моей точки зрения graphql имеет уникальные перимущества которе не может дать на сегодняшний день ни одна другая система.
- Программный код и документация связаны и никогда не могут рассогласоваться
- Клиент сам решает какие поля вернет запрос и какой уровень вложенности объектов ему нужен
- Клиент может в одном запросе запрашивать произвольное количество объектов (веб-разработчики уже свыклись с тем что запросов будет много, а вот для мобильных разраотчиков один запрос — один экран это было бы неплохо)
А так же полностью поддерживаю что ни в коем случае нельзя смешивать уровень бизнес-логики и протокола — это о «краноречивых» статусах. Особенно 404 — это нет зебры в зоопарке, или это нет url «зоопарк»?
Я согласен, что сейчас все вспомогательные библиотеки так или иначе ориентированы на статусы. Но мне кажется логичным использовать максимум три из них: 200 и 304 при нормальном ответе плюс какой-нибудь незарезервированный 400-й статус при ошибке.
С моей точки зрения graphql имеет уникальные перимущества которе не может дать на сегодняшний день ни одна другая система. Программный код и документация связаны и никогда не могут рассогласоваться
Поясните.
По мне так это уже давно норма.
И не вижу чтобы конкретна эта технология тут чем то особенным выделялась.
Где его только нет.
В том же OpenAPI/Swagger, Python, Go — да где угодно.
Но не только это как я уже сказал есть еще преимущества кроме документации:
2) запрос клиента сам определяет какие поля нужно вернуть у объекта и до какого уровня вложенности показывать связанные объекты
3) можно формировать для одного запроса ответ для двух и более разнородных объектов нужных например для отображения на странице мобильного приложения. Дл чего это важно см. habrahabr.ru/post/331120
Хочу еще раз подчеркнуть: что на стороне бэкэнда работет одно и то же описание объекта. Разработчик фронтенда сам решает что ему нужно получить в ответе сервера, имеет большую степень свободы в том как формировать запрос и какие поля и объекты ему нужно получить. (аналогия полнейшая graphQL — sql).
У graphql в принципе все по набору фич очень похоже на OpenAPI. Но разница в том, что если программно был описан объект Х, для него уже готова документация, входные параметры типа Х будут валидироваться на полное соответсвие (что немаловажно для языков без строгой типизации).
Все равно не понял.
В OpenAPI вы можете (да так часто и делают для удобства разработчиков фронтенда) задействовать веб-сервер, который распарзит описание API и отдаст его в не очень красиво оформленной страничке.
И даст поиграться с API даже безо всякого curl, то есть прямо с этой страницы.
Вероятно имеется в виду, что в graphql этот веб-сервер будет парсить непосредственно код, а не описание, которое может не соответствовать коду.
Например если вы сейчас разрабатываете на PHP то вот на Хабре есть статья с простым примером. habrahabr.ru/post/328122
Я про grqphql хорошо знаю, я не уверен про OpenAPI/swagger. По моим представлениям описание API и код его реализующий, там разделены. Пускай в аннотациях описание, но синхронизировать описание и код — обязанность разработчика. Могу ошибаться.
+ как я уже сказал фича с запросами которые гибко управляются клиентом. Что является отдельным и существенным преимуществом.
По-моему, мы об одном и том же говорим. В GraphQL страничка документации генерируется по тому же коду, по которому API будет парсить и валидировать, в OpenAPI, насколько я знаю, страничка генерируется на основании описания реализации, которое гвоздями к ней не прибито, в лучшем случае какие-то линтеры или тесты обнаружат разницу между описанием и реализизацией.
По моим представлениям описание API и код его реализующий, там разделены. Пускай в аннотациях описание, но синхронизировать описание и код — обязанность разработчика. Могу ошибаться.
Вы можете делать это автоматически.
При каждом make.
Разумеется, тогда как только поменяют описание — ваш код сломается.
Но вы это тут же увидите.
И добавите нужную новую функцию в свой код или т.п.
Что это? Какой-то чекер будет статически проверять есть ли реализация какого-то ендпоинта API, описанного в документации, и, хотя бы, принимает ли описанный в ней шейп на вход и отдаёт ли на выходе? Через отдельный типы для каждого шейпа?
Функции которые были в преждней версии API у вас уже реализованы. Поэтому если API не менялся — ничего не сломается.
А новая функция API окажется не реализованной (абстрактно объявленной в заглушке). И вы об этом узнаете.
То есть просто проверка есть ли такой метод без всяких анализов шейпов? добавили/удалили поле в ожидаемый/возвращаемый шейп и никто не узнает?
И если язык с типами — то шанс не заметить ниже.
У меня встречный вопрос про GraphQL.
Тут вот пишут что он «самодокументируемый» и пр. достоинства.
Так ведь все равно если сервер что-то там поменяет, то клиент же ровно так же имеет шанс этого не заметить.
Если логику выполнения запроса/мутации поменяет, то не заметит, да. Но с типами/шейпами всё строго, по крайней мере в основных реализациях — до резолвера (функция/метод непосредственно исполняющие запрос/мутацию) дело не дойдёт, если что-то не соответствует описанию. Можно сказать, что человекочитаемые описания сигнатур генерируются по фактическому их описанию в коде. Фронтендер, например, не заглядывая в код сервера, получает техническое описание API?
гарантированно этому коду соответствующее. А если бэкендер что-то изменит в сигнатурах, то и описание изменится. Формальное описание API не лежит где-то рядом с его реализацией, не генерируются заглушки реализации из описания, а само описание и есть реализация. Одни и те же структуры данных отвечают и за роутинг/валидацию/исполнение запроса, и за генерацию человеко-/машиночитаемого описания. И это by design, а не прикручено сбоку в make.
Если логику выполнения запроса/мутации поменяет, то не заметит, да. Но с типами/шейпами всё строго, по крайней мере в основных реализациях — до резолвера
Тогда это ровно так же как и в gRPC и в Swagger/OpenAPI
GraphQL — это скорее язык, чем протокол. На практике "по умолчанию" использует те же HTTP+JSON как транспортный протокол и тип данных. Собственно различия начинаются где-то на уровне как интерпретировать поля в JSON. То есть, обычно, в RESTish HTTP API тело HTTP-запроса или одного из его полей верхнего уровня — это представление какой-то сущности, а в GraphQL — собственно GraphQL запрос и его параметры.
Моя мечта начать использовать graphql, который тоже REST, но не RESTAPI. С моей точки зрения graphql имеет уникальные перимущества которе не может дать на сегодняшний день ни одна другая система.
GraphQL хорош когда вы сами не знаете что хотите.
Вы просто получаете универсальный язык запросов.
Полной реализацией REST HTTP API обычно никто не занимается чисто из практических соображений, поэтому используется REST-like или RESTish когда нарушают некоторые требования или традиции, сложившиеся вокруг REST.
Ну и язык запросов graphql. Если пытаться сделать RESTAPI выразительнее (через разные параметры типа списка запрашиваемых полей, неободимости разворачивавть связанные объекты, фильтры, пагинацию) — то вся простота и изящества куда-то очень быстро уходит. См. апример www.ibm.com/support/knowledgecenter/SSPLFC_7.2.2/com.ibm.taddm.doc_7.2.2/SDKDevGuide/r_cmdbsdk_restapi_objectclass.html
Я не уверен, поэтому написал "почти". Избегаю кванторов всеобщности, если не уверен.
Под полной реализацией REST HTTP API я имею в виду API, полностью реализующий как стандарт HTTP, так и принципы REST. Я не уверен даже, что есть полные реализации HTTP и следует ли из полной реализации HTTP автоматическое соблюдение REST (скорее нет, чем да).
+ 5.1.2 Client-Server — да http протокол предполагает общение клиент-сервер.
+ 5.1.3 Stateless — да http ничего не знает о состоянии клиента. Веб-приложения «научились» нарушать это правило при помощи сессий. И тут же получили оплеуху в виде невозможности масштабироваться. (Поэтому сейчас все чаще можно встретить сессии в виде GWT когда сессия передается в каждом запросе и может быть таким образом расшернеа между серверами)
+ 5.1.4 Cache. Да в http протоколе определены запросы которые кэшируются и которые не кэшируются и как это реализовать.
± 5.1.5 Uniform Interface.
* identification of resources + да идентифицируются по URI
* manipulation of resources through representations — это как бы не входит в http протокол и может трактоваться как данные и представление это не одно и то же. То есть для простого текста данные==преставление. Для html документа, картинки, скрипта браузер сам знает как отобразить эти данные.
* self-descriptive messages — это тоже не уровень http протокола. Означает что все для обработки сообщение должно содержаться в самом сообщении.
* hypermedia as the engine of application state (HATEOAS) — в RESTAPI почему-то не получило широкого распространения.
+ 5.1.6 Layered System Обращаясь к серверу клиент не знает сколько уровней имеется в архитектуре сервера — ну да.
+ 5.1.7 Code-On-Demand — Все что нужно еще закгружается с сервера по требовнию (да, скрипты же загружаются)
Почему-то ни в одном(!) руководстве не упоминается еще два основополагающих принципа:
5.1.8 Style Derivation Summary — который гласит что эти принципы предназначены для выбора архитектуры приложения (а не способов формирование url).
и те упоминается принцип:
5.1.1 Starting with the Null Style — который гласит что нет других никаких ограничений.
Я к чему это все привел. Мое убеждение что есть REST и есть RESTAPI. И это две разные совершенно несвязанные вещи.
Серверная сессия обычно нарушает Stateless, поскольку часто в сессии хранится дополнительная информация, например userId, необходимая для обработки запроса. Да, бывает, там хранится какая-то информация без которой запрос вполне может быть обработан, например кеш тех же прав пользователя (нет сессии — получим из базы или из сервиса авторизации) или информация для трекинга пользовательского поведения, но обычно всё же там хранится какое-то состояние сильно влияющее на результаты обработки запроса.
manipulation of resources through representations — это как бы не входит в http протокол и может трактоваться как данные и представление это не одно и то же. То есть для простого текста данные==преставление. Для html документа, картинки, скрипта браузер сам знает как отобразить эти данные.
Представление в терминах REST — это не отображение пользователю, а поток байт, отображающий состояние ресурса (5.2.1.2).
self-descriptive messages — это тоже не уровень http протокола. Означает что все для обработки сообщение должно содержаться в самом сообщении.
Отчасти вы правы, но только отчасти. Из 5.3.1 "REST enables intermediate processing by constraining messages to be self-descriptive:… standard methods and media types are used to indicate semantics and exchange information", HTTP довольно подробно описывает стандартные методы как синтаксически, так и семантически.
hypermedia as the engine of application state (HATEOAS) — в RESTAPI почему-то не получило широкого распространения.
Тем не менее имеется.
А вообще я как-то потерял нить обсуждения. Моё убеждение, что есть REST как архитектурные принципы построения масштабируемых распределенных приложений, есть более-менее соответствующие этим принципам приложения, подавляющая часть из которых использует HTTP для предоставления серверного API не только в качестве транспортного протокола, но и прикладного, пытаясь его синтаксис и семантику применять к своей предметной области, все эти DELETE /contracts/123. При этом частенько принципы REST и семантика HTTP нарушаются "по мелочам" типа отправки запросов PUT /contracts/123/approve c телом true. Такие API часто называют REST-like (субъективно характерно для русскоязычного информпространства) или RESTish (англоязычные обычно), имея в виду что они довольно близко подходят как к соблюдению принципов REST в архитектуре, так и соблюдение синтаксиса и семантики HTTP (опуская HTTP для удобства). Также можно встретить REST over HTTP, где HTTP используется лишь в качестве транспортного протокола, практически игнорируя его семантику, учитывая лишь технические возможности и особенности поведения распространенных или целевых компонентов сетевой инфраструктуры таких как браузеры, веб-сервера, прокси-сервера и т. п., при этом обычно меньше отходя от принципов REST. Ну и "over HTTP" часто опускают для краткости тоже, поскольку реализации REST не на базе HTTP встречаются крайне редко.
Получается спор наш чисто терминологический?
семантика HTTP нарушаются «по мелочам» типа отправки запросов PUT /contracts/123/approve c телом true
А где здесь нарушение семантики HTTP?
Предполагается очевидным, что ресурс имеет url /contracts/123, а approve является методом, модифицирующий этот ресурс, непредусмотренным HTTP способом. Особенно, если в результате GET /contracts/123 не получит значение свойства approve=true, а получит, например, свойство status=approved, а GET /contracts/123/approve всегда возвращает 404. Метод PUT, согласно rfc2616, должен сохранять (создавать или модифицировать) ресурс непосредственно, а не инициализировать какие-то процессы обработки данных, прерогатива которых метод POST.
Особенно, если в результате GET /contracts/123 не получит значение свойства approve=true, а получит, например, свойство status=approved, а GET /contracts/123/approve всегда возвращает 404
Если так, то да. Но если представить, что approve не метод, а свойство объекта /contracts/123 и GET /contracts/123/approve будет возвращать true, а GET /contracts/123 {..., «approve»: true, ...}, то все становится на свои места. Только approve стоит в approved переименовать, я не сразу заметил, что это глагол((
К сожалению ни Swagger, ни RAML не могут сгенерить хорошую статичную документацию, которую не стыдно выложить в общий доступ. Особенно если речь идет об отправке данных как multipart/form-data
или application/x-www-form-urlencoded
в теле POST запроса в стиле EntityName[attribute]=value
. OAPI 3 умеет такое в GET параметрах, заголовках, путях и куках, но не в POST :(
(структура EntityName
определена в отдельном месте для переиспользования)
github.com/lord/slate
www.npmjs.com/package/widdershins
github.com/Rebilly/ReDoc
Swagger-UI, по моему мнению пригоден лишь как внутренний инструмент.
Как построить REST-like API в крупном проекте