Первый
Согласно RFC 3986 §3.4 (Унифицированные идентификаторы ресурсов § (Синтаксические компоненты) | Запрос
3.4 Запрос
Компонент запроса содержит неиерархические данные, которые вместе с данными в компоненте пути (раздел 3.3) служат для идентификации ресурса в рамках схемы URI и полномочий по присвоению имен (если таковые имеются).
Компоненты запросов предназначены для поиска неиерархических данных; Есть несколько вещей, более иерархических по своей природе, чем родословная! Ergo - независимо от того, думаете ли вы, что это «REST-y» или нет - для того, чтобы соответствовать форматам, протоколам и структурам и для разработки систем в Интернете, вы не должны использовать строку запроса для идентификации этой информации.
REST не имеет ничего общего с этим определением.
Прежде чем ответить на ваши конкретные вопросы, ваш параметр запроса "search" имеет неправильное имя. Лучше было бы рассматривать ваш сегмент запроса как словарь пар ключ-значение.
Строка запроса может быть более правильно определена как
?first_name={firstName}&last_name={lastName}&birth_date={birthDate}
и т.п.
Чтобы ответить на ваши конкретные вопросы
1) Какой дизайн API более RESTful и почему? Семантически они имеют в виду и ведут себя одинаково. Последний ресурс в URI - это «дети», что фактически означает, что клиент работает с дочерним ресурсом.
Я не думаю, что это так ясно, как вы, кажется, верите.
Ни один из этих интерфейсов ресурсов не является RESTful. Основным условием для RESTful архитектурного стиля является то , что состояние приложения переходы должны быть переданы с сервера в гипермедиа. Люди работали над структурой URI, чтобы сделать их как-то «RESTful URI», но формальная литература о REST на самом деле очень мало говорит об этом. Мое личное мнение таково, что большая часть мета-дезинформации о REST была опубликована с намерением избавиться от старых вредных привычек. (Построение действительно «RESTful» системы на самом деле довольно трудоемко. Индустрия взяла верх над «REST» и наполнила некоторые ортогональные проблемы бессмысленными оговорками и ограничениями.)
В литературе по REST говорится, что если вы собираетесь использовать HTTP в качестве протокола приложения, вы должны придерживаться формальных требований спецификаций протокола, и вы не можете «создать http по ходу и при этом заявить, что используете http» ; если вы собираетесь использовать URI для идентификации своих ресурсов, вы должны придерживаться формальных требований спецификаций, касающихся URI / URL.
Ваш вопрос адресован непосредственно RFC3986 §3.4, который я связал выше. Суть в том, что даже если соответствующего URI недостаточно для того, чтобы считать API «RESTful», если вы хотите, чтобы ваша система действительно была «RESTful», и вы используете HTTP и URI, то вы не можете идентифицировать иерархические данные через Строка запроса, потому что:
3.4 Запрос
Компонент запроса содержит неиерархические данные
... это так просто.
2) Каковы плюсы и минусы каждого с точки зрения понятности с точки зрения клиента и ремонтопригодности с точки зрения дизайнера.
«Плюсы» первых двух в том, что они на правильном пути . «Минусы» третьего - это то, что он кажется неправильным.
Что касается вашей понятности и удобства сопровождения, то они определенно субъективны и зависят от уровня понимания разработчика клиента и выбора дизайна дизайнера. Спецификация URI является окончательным ответом относительно того, как URI должны быть отформатированы. Предполагается, что иерархические данные представлены на пути и с параметрами пути. Неиерархические данные должны быть представлены в запросе. Фрагмент является более сложным, потому что его семантика зависит конкретно от типа носителя запрашиваемого представления. Итак, чтобы рассмотреть компонент «понятности» вашего вопроса, я попытаюсь перевести именно то, что на самом деле говорят ваши первые два URI. Затем я попытаюсь представить то, что, как вы говорите, вы пытаетесь выполнить с помощью действительных URI.
Перевод ваших дословных URI в их семантическое значение.
/myservice/api/v1/grandparents/{grandparentID}/parents/children?search={text}
Это говорит о том, что для родителей бабушек и дедушек найдите, что их ребенок имеет то, search={text}
что вы сказали с помощью вашего URI, только связно, если вы ищете родных братьев и дедушек. С вашими «бабушкой и дедушкой, родителями, детьми» вы обнаружили, что «дедушка и бабушка» прошли поколение к своим родителям, а затем вернулись к поколению «бабушки и дедушки», посмотрев на детей родителей.
/myservice/api/v1/parents/{parentID}/children?search={text}
Это говорит о том, что для родителя, идентифицированного {parentID}, найдите, что у его потомка есть ?search={text}
Это ближе к тому, что вы хотите, и представляет отношение родитель-потомок, которое, вероятно, может использоваться для моделирования всего API. Чтобы смоделировать это таким образом, на клиента ложится бремя, чтобы признать, что если у него есть «grandparentId», то существует слой косвенности между идентификатором, который он имеет, и той частью семейного графа, которую он хочет видеть. Чтобы найти «child» с помощью «grandparentId», вы можете позвонить в свою /parents/{parentID}/children
службу, а затем найти каждого ребенка, которого вернули, и найти среди своих детей идентификатор вашей личности.
Реализация ваших требований в виде URI.
Если вы хотите смоделировать более расширяемый идентификатор ресурса, который может обходить дерево, я могу придумать несколько способов, которыми вы можете это сделать.
1) Первый, о котором я уже говорил. Представьте граф «Люди» в виде составной структуры. У каждого человека есть ссылка на поколение над ним через его путь Родителей и на поколение под ним через его путь Детей.
/Persons/Joe/Parents/Mother/Parents
был бы способ схватить бабушку и дедушку Джо по материнской линии.
/Persons/Joe/Parents/Parents
был бы способ схватить всех бабушек и дедушек Джо.
/Persons/Joe/Parents/Parents?id={Joe.GrandparentID}
схватил бы бабушку и дедушку Джо с идентификатором у вас в руках.
и все это имело бы смысл (обратите внимание, что здесь может быть снижение производительности в зависимости от задачи путем принудительной установки dfs на сервере из-за отсутствия идентификации филиала в шаблоне «Родители / Родители / Родители».) Вам также полезно иметь способность поддерживать любое произвольное количество поколений. Если по какой-то причине вы хотите посмотреть на 8 поколений, вы можете представить это как
/Persons/Joe/Parents/Parents/Parents/Parents/Parents/Parents/Parents/Parents?id={Joe.NotableAncestor}
но это приводит ко второму доминирующему варианту представления этих данных: через параметр пути.
2) Использование параметров пути для «запроса иерархии». Вы можете разработать следующую структуру, чтобы облегчить нагрузку на потребителей, и при этом иметь API, который имеет смысл.
Чтобы оглянуться назад на 147 поколений, представление этого идентификатора ресурса с параметрами пути позволяет сделать
/Persons/Joe/Parents;generations=147?id={Joe.NotableAncestor}
Чтобы отыскать Джо у его прародителя, вы можете посмотреть на график известного количества поколений для идентификатора Джо.
/Persons/JoesGreatGrandparent/Children;generations=3?id={Joe.Id}
При использовании этих подходов следует отметить, что без дополнительной информации в идентификаторе и запросе следует ожидать, что первый URI извлекает человека из поколения 147 от Джо с идентификатором Joe.NotableAncestor. Вы должны ожидать, что второй заберет Джо. Предположим, что вы действительно хотите, чтобы вызывающий клиент мог получить весь набор узлов и их взаимосвязи между корневым лицом и конечным контекстом вашего URI. Вы можете сделать это с тем же URI (с некоторым дополнительным оформлением) и настройкой Accept of text/vnd.graphviz
on по вашему запросу, который является зарегистрированным IANA типом носителя для .dot
представления графа. С этим измените URI на
/Persons/Joe/Parents;generations=147?id={Joe.NotableAncestor.Id}#directed
с заголовком HTTP-запроса,
Accept: text/vnd.graphviz
и вы можете достаточно четко проинформировать клиентов о том, что им нужен ориентированный граф иерархии поколений между Джо и 147 поколениями до того, когда это 147-е поколение предков содержит человека, идентифицированного как «Известный предок» Джо.
Я не уверен, что text / vnd.graphviz имеет какую-либо заранее определенную семантику для своего фрагмента, я не мог найти ни одной в поиске инструкции. Если этот тип мультимедиа действительно имеет предварительно определенную информацию о фрагменте, то его семантика должна соблюдаться для создания соответствующего URI. Но, если эта семантика не предопределена, спецификация URI заявляет, что семантика идентификатора фрагмента не ограничена и вместо этого определяется сервером, что делает это использование допустимым.
3) Для чего действительно используются строки запроса, помимо «фильтрации» на вашем ресурсе? Если вы выберете первый подход, параметр фильтра будет встроен в сам URI как параметр пути, а не как параметр строки запроса.
Я полагаю, что уже полностью избил это до смерти, но строки запроса не для "фильтрации" ресурсов. Они предназначены для идентификации вашего ресурса по неиерархическим данным. Если вы углубились в иерархию, указав свой путь,
/person/{id}/children/
и хотите указать конкретный дочерний элемент или определенный набор дочерних элементов, вы должны использовать какой-либо атрибут, который применяется к идентифицируемому набору, и включить его в запрос.