Pull to refresh

Comments 48

Про обработку ошибок и поведение в неоднозначных ситуациях?
Типа при удалении что происходит с теми кто ссылается на удаляемый объект? delete cascade?

Я посчитал, что для вводной статьи это много. Но если кого интересует, то напишу тут:

При удалении сервис должен сам проверить и обновить ссылаемые/ссылающиеся сущности: удалить их, поставить null или значение по умолчанию. Выбор действия зависит от ограничения в обозначенного в схеме. Это не относится к НЕ внешним ключам.

Например, если сущность, на которую мы ссылаемся ключом fk_id = '11111', удалится, то сервис обязан обновить наш ссылающийся ключ (в зависимости от типа отношений: проставить null, или удалить нас самих). Но если это поле зависит от другой сущности по бизнес логике не прописанной в схеме, то сервис не обязан делать обновления.

Грубо говоря, поведение при удалении описывается схемой.

https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part1-protocol.html#sec_DeleteanEntity

Опять XML. Не хочу. С мылом было не вкусно, с odata вкуснее не будет.

Возникла безумная идея... А что если использовать YAML? По крайней мере для всяких конфигов прошла эволюция XML -> JSON -> YAML. Почему бы и для API его не использовать?

Достоинство YAML перед JSON — в человекочитаемости. Нет никакого смысла использовать его в задачах, в которых как читателем, так и писателем является машина.

XML и JSON тоже человекочитаемые по сравнению, например, с protobuf. Вся разница в скобочках: угловые, фигурные, отсутствуют :)

Полистал. Во-первых на русском. Не взлетит. 97-98% населения планеты его не знают и учить не планируют.

Во-вторых экранирование ужасное. Как выглядит Tree внутри которого строка, содержащая в себе Tree, внутри которой находится, например, строка с yaml с многострочным литералом внутри которого докстринг с примером Tree?

Во-первых на русском.

Во вторых на английском.

Как выглядит Tree внутри которого строка, содержащая в себе Tree

hello
	some \
	tree \
		\hello
		\	another \
		\	tree \
		\		\hello:
		\		\  yaml: ""
		\		\  too: |-
		\		\    hello
		\		\    	tree \
		\		\    	again \
		\		\    		\plain
		\		\    		\text

Смысла в этом мало, конечно.

Во. Сравните с это с yaml'ом.

nested_yaml: |
  another_nested: |
    and_random_gibberish: |
        j: :ff :
            - fsdfjd : sdlfdf
        - sdlkfsld "

multiline в yaml - это то, за что можно простить on/false/yes.

Сравнил:

  • Сложно отследить, где закончился ямл и началась строка - нужно смотреть концы всех предыдущих строк. Подсветка синтаксиса есть не везде, особенно, если сам ямл находится в строке.

  • Элементы списка идут на том же уровне отступа, что и ключи, что усложняет ориентирование. Дефисы только путают.

  • Что происходит на 4 строке мало кто сможет ответить правильно.

Это всё такая мелочь на фоне проблемы "вставить блок в формат..."

Для того, чтобы вставить в Tree мне нужно будет менять каждую строку. Взрывая мозг себе и окружающим (если это повторная вставка вложенного текста).

Если вы думаете, что это редкий сценарий...

Представьте себе плейбуку ансибла (yaml), засылающего параметром в модуль os_server userdata (который тоже в yaml).

- name: Run server
  os_server:
    name: my_server
    flavor: m5.medium
    userdata: |
      #cloud-config
      write_files:
       - path: /etc/sysconfig/samba
         content: |
           # My new /etc/sysconfig/samba file

           SMBDOPTIONS="-D"
  notify: start samba

Это я не придумываю, пример cloud-init - из официальной документации cloud-init'а.

Для меня это настолько киллерфича, что любой формат, который потребует реформатирования, автоматически дисквалифицируется.

чтобы вставить в Tree мне нужно будет менять каждую строку

Её в любом случае менять - надо добавить отступы. А если за вас это делает редактор, то и бэкслеши он добавить в состоянии.

Представьте себе плейбуку ансибла (yaml), засылающего параметром в модуль os_server userdata (который тоже в yaml)

Представил:

Сдвиг пробелами - наименее инвазивная форма, которая справляется с leading spaces и сохраняет общую структуру отступов.

Простите, мне реально надо вставлять '\' в середину строки? Это не опечатка? `todo \switch after release 911`?

А что делать, если у меня строка начинается с '\'? Удваивать?

---
- hosts: localhost
  tasks:
    - shell:
        /bin/echo -e
           "
             \\e[33;1m
             \\\\ - is a backslash
           "> /dev/tty

      

мне реально надо вставлять '\' в середину строки? Это не опечатка? `todo \switch after release 911`?

Слева от бэкслеша структурные узлы, справа - сырые данные.

что делать, если у меня строка начинается с '\'? Удваивать?

Не важно с чего она начинается, в том-то и дело. Бэкслеш явно показывает начало сырых данных. Можете туда хоть бинарник засунуть.

Для меня в S-expressions такие конструкции выглядят понятнее. А ещё там есть quasiquote, unquote, unquote-splicing, позволяющие, например, в середину шаблона вставлять переменные. Для конфигов это выглядит интересной фичей. Может цикл наконец замкнется и люди вернутся к круглым скобочкам...

Я не видел много API с yaml внутри. Обычно yaml - это для человеков. За вычетом нескольких интересных типов, они близки (если быть точным, json - подмножество yaml), и API обычно на json'е неплохо себя чувствует. Обычно от json уходят в сторону протобафа, потому что так сильно быстрее.

Уходить от json в сторону xml - это делать всё одновременно нечитаемым, сложным для компьютеров (всякого рода xml bombs и т.д.), и оставаться медленным.

Так там же говорится, что схему можно не только в виде XML описывать, но и в виде JSON.

У XML есть киллер фича - к нему можно подцепить XSLT который при открытии ссылки из браузера нарисует разрабу красивый UI. Вот тут я делал пример. Странно, что я этого нигде не видел.

Для меня киллер-фича XML — что можно указать XSD-схему прямо в тексте документа, и что современные IDE умеют их парсить и включать автодополнение и подсвечивать некорректные токены.

Если у какого-то языка разметки данных нет столь же лёгкой в понимании воможности подключить схему, то он неудобен для меня.

Для JSON аналогом является JSON Schema и атрибут $schema в корне документа. Это как-раз даёт возможность IDE налету валидировать документ и делать автодополнение

Да, для API это называется swagger. Зачем это на уровень браузера вытащить?

Если вы про сваггер инспектор, то это отдельный тул, через который нужно открывать ссылку. Это не так удобно, как просто переход по ней. К тому же он тупо раскрашенный json показывает, а это куча визуального шума, многострочный текст в одну строку, нет гиперссылок на связанные ресурсы, поиска и так далее.

Вроде все неплохо, но стандарт вообще не популярный, и кмк не развивается. Даже на сайте https://www.odata.org/blog/ последняя запись 2018 года.

Раньше был норм. Но по моим ощущениям за пределами .NET он не особо распространился. Не очень понятна его ниша сейчас, когда какие-нибудь GraphQL или HATEOAS решают похожие задачи.

Э-э-э, сравнение с GraphQL тут понятно (решается одна и та же задача), но HATEOAS-то тут каким боком?

И то и другое это как минимум REST API 2-го уровня. Т.е. они даже ближе друг к другу, чем к GraphQL.

В .NET можно описать схему данных и получить готовое OData API. И, например, в Spring можно описать схему данных, репозитории данных и получить готовое HATEOAS API через Spring Data. Ну, т.е. общая решаемая задача - получить API на основе схемы данных с минимальными усилиями.

В самом HATEOAS как в подходе этого нет, но Spring'овая реализация предоставляет разные стандартные ссылки типа получения следующей страницы для коллекции объектов и т.п. Т.е. в обоих случаях решаемая задача - это стандартизация API. Не нужно изобретать свои запросы для пагинации, сортировки.

Другая общая задача - предоставление метаданных. По крайней мере в Spring реализации есть profile-ссылки. HAL Forms из той же области, хотя я ими никогда не пользовался.

Ещё в статье упоминается следующее:

OData определяет функции (Function) и действия (Action)

Function - это операция над ресурсами, которая обязательно возвращает значение и не имеет сторонних эффектов.

Action - это операция, которая может изменить значение

Я не помню, чтобы я пользовался этим в OData, но в HATEOAS это основная фишка. Т.е. общая задача, решаемая этими протоколами - это предоставлении информации о том, какие действия доступны для ресурса, в какое новое состояние его можно перевести.

На мой взгляд у этих протоколов много пересечений. Там где в .NET используется OData, в Java скорее всего будет использоваться HATEOAS.

Вы как-то сильно путаете HATEOAS и детали спринговой реализации. HATEOAS — это, в терминах той статьи на которую вы привели ссылку, основной принцип REST 3 уровня, и больше этот термин не означает ничего.


HATEOAS не предусматривает ни коллекций сущностей, ни стандартной формы запросов к ним, ни даже машиночитаемой схемы!

Я согласен, HATEOAS - это абстрактный подход в абстрактном вакууме, в котором практически ничего нет. Но есть в разных форматах, построенных на нём. В Siren даже сущности и коллекции упоминаются. В HAL-FORMS много метаданных. Хотя всё это и на много проще, чем OData.

Это конечно слишком абстрактная аналогия. Но всё-таки даже если соотносить чистый HATEOAS и OData, всё равно в основе одна идея, что сервер передаёт клиенту какие-то метаданные. В случае OData - это схема данных, действия и функции. В случае HATEOS - это ссылки (по смыслу аналог действий и функций из OData). А затем пользователь принимает решение куда перейти дальше (к какой сущности или по какой ссылке) и переходит. Даже схема данных в OData с одной стороны конечно машиночитаемая, но всё равно решение о том какие данные запрашивать принимает пользователь.

На мой взгляд всё-таки идея одна. Клиент меньше привязан к серверу, более универсальный. У пользователя есть возможность, грубо говоря, "напрямую" получать информацию от сервера о том, что он может делать.

А отличия в том, что, да, в спецификации OData описано дофига всего. А HATEOAS - это практически просто идея, даже формат ссылок не определен, при этом более конкретные вещи определены уже в отдельных спецификациях (HAL, HAL-FORMS, Siren, ...).

Честно говоря, с некоторой натяжкой я бы даже назвал OData одной из реализаций HATEOAS :)

Я согласен, HATEOAS — это абстрактный подход в абстрактном вакууме, в котором практически ничего нет. Но есть в разных форматах, построенных на нём. В Siren даже сущности и коллекции упоминаются. В HAL-FORMS много метаданных. Хотя всё это и на много проще, чем OData

Ну так и сравнивайте с OData эти самые Siren и HAL-FORMS (вон, у них даже названия есть!), зачем приплетать сюда HATEOAS?


Честно говоря, с некоторой натяжкой я бы даже назвал OData одной из реализаций HATEOAS :)

Со слишком большой натяжкой. Для HATEOAS недостаточно положить метаданные где-то рядом, все допустимые ресурсы должны быть доступны по ссылкам начиная с некоторого корня (или корней). Фактически, HATEOAS несовместим с любым языком запросов.

Да даже в .NET нынче с ним связываться - боль и страдание. Мы года полтора назад по глупости пытались новый проект на нем запустить, поплевались и переписали на web api с нужными самописными обертками все. Odata нынче больше мертв чем жив и "автор не нашел "краш курсов" по OData и пришлось собирать знания по кусочкам" как раз очень хорошая демонстрация этого.

А вы хотите, чтобы стандарт менялся каждый месяц?

Я сомневаюсь, что стандарт на столько отличный, что за 4 года там не нашли, что исправить или добавить.

А чего там исправлять и добавлять-то?


Для сравнения, в стандарте JSON уже 5 лет не могут найти чего исправить.

За статью спасибо, хорошо знать, чего надо остерегаться. Вот серьезно, зачем столько сложностей. Тут и схема на xml, а ответы в json. Запросы Get-ом, которые могут закешироваться. SQL прямо в запросе. Столько всего намешано, что получился какой-то монстр.

Э-э-э, а где тут sql прямо в запросе? Или теперь любой язык запросов — это sql?

Можно делать запросы к ресурсам прямо в строке запроса. Для запросов есть свой специальный язык. По фукнционалу он очень похож на SQL.

Не чистый SQL само собой, но теперь тебе помимо SQL нужно ещё знать как Odata его интерпретирует в SQL (у odata ведь ещё пока нет своего встроенного движка субд?), и делает ли он это оптимально, или как обычный ORM - через жопу.

Начинали с неплохой api которая задумывалась "хорошей заменой других restfull", закончили встроенным ORM.

Учитывая, что вложенные запросы в условии $filter (оно же where), как и какая-нибудь группировка, не поддерживаются — накосячить в трансляции запросов затруднительно.

запросы в условии $filter (оно же where)
Есть хоть одна причина, почему WHERE нужно было называть словом $filter?
$filter=approvalInstanceId ne null
И почему NOT NULL здесь решили писать ne null? Как-будто кто-то сделал транслитерацию перевода учебника по SQL.
Почему так важен not null? Потому-что в SQL NULL это не пустая строка. И это не 0. Это отдельное значение. Для SQL where NOT NULL… Как его обрабатывает OData в этом случае? Я не знаю, ведь авторы таким образом могли сделать всё что угодно. Я видел такие ORM, которые сначала делают SELECT *, а все условия парсят уже на своей стороне. А очень популярная в PHP ORM Eloquent — если делать нежадный JOIN таблиц, то он сначала выберет все ИД из одной таблицы, а затем вставит их в WHERE IN второй. Никакого вам INNER JOIN.
Можно сделать несколько запросов: сначала массив табелей, затем для каждого — запрос на информацию о пользователе. Но это лишнее. Можно ведь сделать просто — добавить $expand:
Почему $expand а не join? Опять-же, у меня было 100500 случаев, когда нужно в условии JOIN прописать сразу два условия (Join), как здесь их прописать? Не уверен, что такое возможно, если только не дописывать в $filter (но возможно не пройдет валидацию).
Короче сделали свой SQL, который делает тоже самое что и SQL, но писать надо на другом языке. Ну и тема с доступом к данным не раскрыта — буквально сразу такая API потребует ограничения со всех сторон, и проще будет писать свои запросы на сервере, чем позволять это делать пользователю.

По поводу ne кмк это аббревиатура от not equal. Встречал много где.

Есть хоть одна причина, почему WHERE нужно было называть словом $filter?

А есть хоть одна причина, почему оператор фильтрации нужно было называть словом WHERE? :-)


И почему NOT NULL здесь решили писать ne null? Как-будто кто-то сделал транслитерацию перевода учебника по SQL.

Потому что not equals null.


Как его обрабатывает OData в этом случае? Я не знаю, ведь авторы таким образом могли сделать всё что угодно. Я видел такие ORM, которые сначала делают SELECT *, а все условия парсят уже на своей стороне. А очень популярная в PHP ORM Eloquent — если делать нежадный JOIN таблиц, то он сначала выберет все ИД из одной таблицы, а затем вставит их в WHERE IN второй. Никакого вам INNER JOIN.

А это для клиента не важно, это проблема того кто пишет бэк.


Кстати, WHERE IN вместо INNER JOIN — совершенно корректный способ соединения коллекций если они лежат на разных серверах СУБД.


И да, сделать SELECT * и парсить на стороне бэкенда — это, конечно, зашквар для ORM, но всё ещё валидный случай для OData. Заметьте: несмотря на повышенное потребление ресурсов, траффик до клиента всё ещё экономится. Просто представьте что было бы, если бы ещё и клиенту результаты SELECT * выдавались...


Ну и тема с доступом к данным не раскрыта — буквально сразу такая API потребует ограничения со всех сторон

Не потребует, потому что доступ тут настраивается настолько элементарно что я не понимаю зачем вообще об этом говорить: просто на этапе формирования запроса к БД добавляем в блок WHERE условие что присутствует доступ к записи.

Ах да, что-то не сразу заметил:


Почему $expand а не join?

Потому что в OData вы не джойните произвольные коллекции, а раскрываете вложенные записи.


Опять-же, у меня было 100500 случаев, когда нужно в условии JOIN прописать сразу два условия (Join), как здесь их прописать?

Никак. Обращайтесь к бекендеру, пусть даст вам отдельное API для такого хитрого запроса.

Проблема ОДаты - в том что она протокол, а не решение.

Типа большие дяди с Майкрософт во главе порешали и сказали "Слушайте, а как было бы здорово, если бы все бизнес-программы могли обмениваться данными примерно одинаково, ну чтобы ты знал что можешь ожидать? Во здорово-то будет".

Реализация же этого всё равно ложится на разработчика. Сейчас есть решения по типу Hasura и Postgraphile, которые простраивают прямо из БД доступ ко всем сущностям, и даже по более простому стандарту OpenAPI. Полный REST автоматически и бесплатно.

Так что ОДата, к сожалению, немного сейчас не нужна уже.

P.S. Но за то, что 1С её поддерживает, за это разработчикам ОДаты низкий поклон - работать с данными в 1С так гораздо удобнее.

P.P.S. Одата может отдавать и JSON, просто дописываешь к строке запроса &$format=json

Работали с одатой без XML.

Все прекрасно дружит с Core WEB Api и работает на JSON.

Но вещь специфическая, да можно быстро на БД повешать апи для работы со всеми сущностями. Но тут подключаются архитекторы с разными принципами и вот волшебная одата в них уже не совсем вписывается.

Делили все на сервисы: Сервис работы со сделками ,сервис работы с контрагентами ...
Сервис работы со сделкой, например имеет методы работы с сущностями "Государственный контракт", "Муниципальный контракт" атрибуты и логика различается, а в базе это один и тотже набор таблиц. В итоге ОДАТА какбы с сырыми данными работает.
Ну и вы забыли что можно через такой апи запулить запрос типа "Select * from database" от которого сервер БД прикурит. И надо сразу ограничения прописывать (механизм кстати есть в odata) типа нельзя все поля селектитить, всегда ограничение на размер выборки, и т.п.

Сервис работы со сделкой, например имеет методы работы с сущностями "Государственный контракт", "Муниципальный контракт" атрибуты и логика различается, а в базе это один и тотже набор таблиц. В итоге ОДАТА какбы с сырыми данными работает.

Это кто-то где-то недоработал. Нет никаких проблем сделать две разные коллекции "Государственный контракт" и "Муниципальный контракт", отображая их на одну и ту же таблицу с разными фильтрами.

А ОДата тут при чём? Те же проблемы у вас были бы и с любым другим автоматическим адаптером к субд.

Sign up to leave a comment.

Articles