Подтвердить что ты не робот

Различные представления RESTful того же ресурса

Мое приложение имеет ресурс в /foo. Обычно он представлен полезной нагрузкой HTTP-ответа следующим образом:

{"a": "some text", "b": "some text", "c": "some text", "d": "some text"}

Клиент не всегда нуждается во всех четырех членах этого объекта. Что означает RESTfully semantic, чтобы клиент мог сообщить серверу, что ему нужно в представлении? например если он хочет:

{"a": "some text", "b": "some text", "d": "some text"}

Как это сделать GET? Некоторые возможности (я искал исправление, если не понял REST):

  • GET /foo?sections=a,b,d.
    • Строка запроса (называемая как строка запроса после всех), похоже, означает "найти ресурсы, соответствующие этому условию и рассказать мне о них", а не "представлять этот ресурс мне в соответствии с этой настройкой".
  • GET /foo/a+b+d Моя любимая , если семантика REST не охватывает эту проблему, из-за ее простоты.
    • Прерывает непрозрачность URI, нарушая HATEOAS.
    • Кажется, нарушить различие между ресурсом (единственным значением URI является идентификация одного ресурса) и представление. Но это спорно, потому что в соответствии с /widgets, представляющий презентабельный список /widget/<id> ресурсы, которые я никогда не имел проблем с.
  • Ослабьте мои ограничения, ответьте на GET /foo/a и т.д., и попросите клиента сделать запрос на компонент /foo, который он хочет.
    • Умножает накладные расходы, которые могут стать кошмаром, если /foo содержит сотни компонентов, а клиенту - 100.
    • Если я хочу поддерживать HTML-представление /foo, я должен использовать Ajax, что является проблематичным, если я просто хочу, чтобы одна страница HTML, которую можно обходить, отображала минималистскими браузерами и т.д.
    • Чтобы поддерживать HATEOAS, он также требует, чтобы ссылки на эти "подресурсы" существовали в других представлениях, возможно, в /foo: {"a": {"url": "/foo/a", "content": "some text"}, ...}
  • GET /foo, Content-Type: application/json и {"sections": ["a","b","d"]} в теле запроса.
    • Unbookmarkable и uncacheable.
    • HTTP не определяет семантику тела для GET. Это законный HTTP, но как я могу гарантировать, что какой-то пользовательский прокси не отделяет тело от запроса GET?
    • My клиент REST не позволит мне поместить тело в запрос GET, чтобы я не мог используйте это для тестирования.
  • Пользовательский HTTP-заголовок: Sections-Needed: a,b,d
    • Я предпочел бы избежать пользовательских заголовков, если это возможно.
    • Unbookmarkable и uncacheable.
  • POST /foo/requests, Content-Type: application/json и {"sections": ["a","b","d"]} в теле запроса. Получите 201 с помощью Location: /foo/requests/1. Тогда GET /foo/requests/1 для получения желаемого представления /foo
    • неуклюжим; требуется обратный код и какой-то странный код.
    • Unbookmarkable и uncacheable, поскольку /foo/requests/1 - это просто псевдоним, который будет использоваться только один раз и сохраняется только до его запроса.
4b9b3361

Ответ 1

Я решил следующее:

Поддержка нескольких комбинаций элементов. Я придумаю имя для каждой комбинации. например если в статье есть члены для автора, даты и тела, /article/some-slug вернет все это, а /article/some-slug/meta просто вернет автора и дату.

Поддержка многих комбинаций:. Я разделяю имена членов по дефисам: /foo/a-b-c.

В любом случае, я вернусь 404, если комбинация не поддерживается.

Архитектурное ограничение

REST

Идентификация ресурсов

Из определения REST:

ресурс R - временно изменяющаяся функция принадлежности M R (t), которая для времени t отображает в набор сущностей или значений, которые эквивалентны. Значениями в наборе могут быть представления ресурсов и/или идентификаторы ресурсов.

Представление, являющееся телом HTTP, а идентификатор - URL.

Это очень важно. Идентификатор - это просто значение, связанное с другими идентификаторами и представлениями. Это отличается от отображения идентификатора → отображения. Сервер может отображать любой идентификатор, который он хочет для любого представления, если оба связаны одним и тем же ресурсом.

Чтобы разработчик разработал определения ресурсов, которые разумно описывают бизнес, рассматривая категории таких вещей, как "пользователи" и "сообщения".

HATEOAS

Если мне действительно нужен идеальный HATEOAS, я могу разместить гиперссылку где-нибудь в представлении /foo до /foo/members, и это представление просто будет содержать гиперссылку на каждую поддерживаемую комбинацию членов.

HTTP

Из определение URL-адреса:

Компонент запроса содержит неиерархические данные, которые вместе с данными в компоненте пути служат для идентификации ресурса в рамках схемы URI и полномочий именования (если есть).

So /foo?sections=a,b,d и /foo?sections=b - разные идентификаторы. Но они могут быть связаны внутри одного и того же ресурса, а отображаются для разных представлений.

HTTP 404 code означает, что сервер не смог найти что-либо, чтобы сопоставить URL, а не то, что URL-адрес не связан с любой ресурс.

Функциональность

У браузера или кеша никогда не будет проблем с косой чертой или дефисами.

Ответ 2

Я бы предложил решение querystring (ваше первое). Ваши аргументы против других альтернатив - хорошие аргументы (и те, на которые я столкнулся на практике при попытке решить ту же проблему). В частности, решение "ослабить ограничения/отвечать на foo/a" может работать в ограниченных случаях, но вводит большую сложность в API как из реализации, так и из-за потребления, и, по моему опыту, не стоит того.

Я слабо буду противостоять вашему аргументу "похоже, значит" с общим примером: рассмотрите ресурс, который представляет собой большой список объектов (GET /Customers). Совершенно разумно размещать страницы этих объектов, и для этого просто использовать эту строку: GET /Customers?offset=100&take=50 в качестве примера. В этом случае запрос не фильтрует на какое-либо свойство указанного объекта, он предоставляет параметры для под-представления объекта.

Более конкретно, я бы сказал, что вы можете поддерживать согласованность и HATEOAS через эти критерии для использования запроса:

  • возвращаемый объект должен быть тем же объектом, что и возвращенный из Url без запроса.
  • Uri без querystring должен возвращать полный объект - надмножество любого вида, доступного с запросом в том же Uri. Итак, если вы кешируете результат неупорядоченного Uri, вы знаете, что у вас есть полная сущность.
  • результат, возвращаемый для заданного запроса, должен быть детерминированным, так что Uris с querystrings легко кэшируется

Однако, что вернуть для этого Уриса иногда могут возникать более сложные вопросы:

  • возврат другого типа объекта для Uris, отличающегося только запросом, может быть нежелательным (/foo является объектом, но foo/a является строкой); альтернативой является возвращение частично заполненного объекта
  • Если вы используете разные типы сущностей для подзапросов, тогда, если ваш /foo не имеет a, статус 404 вводит в заблуждение (/foo существует!), но пустой ответ может быть одинаково запутанным
  • возврат частично заполненного объекта может быть нежелательным, но возвращающая часть объекта может быть невозможна или может быть более запутанной.
  • возврат частично заполненного объекта может быть невозможным, если у вас сильная схема (если a является обязательным, но клиент запрашивает только b), вы вынуждены возвращать либо значение нежелательной почты для a, либо недопустимый объект)

В прошлом я попытался разрешить это, указав определенные именованные "представления" требуемых сущностей и разрешив такие строки, как ?view=summary или ?view=totalsOnly - ограничение количества перестановок. Это также позволяет определить подмножество объекта, которое "имеет смысл" для потребителя услуги и может быть задокументировано.

В конечном счете, я думаю, что это сводится к проблеме согласованности больше всего: вы можете легко встретить руководство HATEOAS, используя запрос, но выбор, который вы делаете, должен быть последовательным в вашем API и, я бы сказал, хорошо документированы.

Ответ 3

На самом деле это зависит от функциональности ресурса. Если, например, ресурс представляет собой сущность:

/customers/5

Здесь "5" представляет собой идентификатор клиента

Ответ:

{
   "id": 5,
   "name": "John",
   "surename": "Doe",
   "marital_status": "single",
   "sex": "male",
   ...
}

Итак, если мы рассмотрим его внимательно, каждое свойство json фактически представляет поле записи в экземпляре ресурса клиента. Предположим, что потребитель хотел бы получить частичный ответ, то есть часть полей. Мы можем посмотреть на это, так как потребитель хочет иметь возможность выбирать различные поля по запросу, которые ему интересны, но не более (чтобы сохранить трафик или производительность, если часть полей трудно вычислить).

Я думаю, что в этой ситуации наиболее читаемым и правильным API будет (например, получить только имя и имя)

/customers/5?fields=name,surename

Ответ:

{
   "name": "John",
   "surename": "Doe"
}

HTTP/1.1

  • Если запрошено недопустимое имя поля - возвращается 404 (не найдено)
  • если запрашиваются разные имена полей - будут генерироваться разные ответы, которые также выравниваются с кешированием.
  • Минусы: если запрашиваются те же поля, но порядок отличается от полей (скажем: fields=id,name или fields=name,id), хотя ответ один и тот же, эти ответы будут кэшироваться отдельно.

HATEOAS

  • По-моему, чистый HATEOAS не подходит для решения этой конкретной проблемы. Потому что для достижения этого вам нужен отдельный ресурс для каждой перестановки комбинаций полей, который является излишним, поскольку он сильно раздувает API (говорят, что у вас есть 8 полей в ресурсе, вам понадобятся перестановки enter image description here!).
  • Если вы моделируете ресурсы только для полей, но не для всех перестановок, это имеет последствия для производительности, например. вы хотите довести количество раундов до минимума.

Ответ 4

Если a, b, c являются свойством ресурса, такого как admin для свойства role, правильным способом является использование первого способа, который вы предложили GET /foo?sections=a,b,d, потому что в этом случае вы применили бы фильтр к foo коллекция. В противном случае, если a, b и c являются синовым ресурсом коллекции foo, то способ, которым следует следовать, - сделать серию GET запросов /foo/a /foo/b /foo/c. Этот подход, как вы сказали, имеет высокую полезную нагрузку для запроса, но это правильный способ следовать подходу Restfull. Я бы не использовал второе предложение, сделанное вами, потому что плюс char в URL имеет особое значение.

Другое предложение - отказаться от использования GET и POST и создать действие для коллекции foo следующим образом: /foo/filter или /foo/selection или любой глагол, представляющий действие в коллекции. Таким образом, имея тело запроса после запроса, вы можете передать json-список ресурса, который вы хотели бы.

Ответ 5

вы можете использовать второй тип медиа-носителя в приложении заголовка запроса /vnd.com.mycompany.resource.rep2, вы не можете добавлять его в закладки, однако параметры запроса не кэшируемы (/foo? sections = a, b, c) вы можете взглянуть на матричные параметры, однако по этому вопросу они должны быть кэшируемыми Параметры матрицы URL по сравнению с параметрами запроса