Как смоделировать «ресурс» прогресса в REST API? - PullRequest
0 голосов
/ 19 июня 2019

У меня есть следующая структура данных, которая содержит массив sectionIds. Они хранятся в том порядке, в котором они были завершены:

applicationProgress: ["sectionG", "sectionZ", "sectionA"]

Я бы хотел сделать что-то вроде:

GET /application-progress - ожидается: секция G, секция Z, секция A

GET /application-progress?filter[first]=true - ожидается: секция G

GET /application-progress?filter[current]=true - ожидается: секция A

GET /application-progress?filter[previous]=sectionZ - ожидается: секция G

Я оценил, что вышеуказанные URL-адреса неверны, но я не уверен, как их назвать / структурировать, чтобы получить ожидаемые данные, например. Ресурсы здесь "sectionids"?

Я бы хотел придерживаться спецификации JSON: API.

UPDATE

Я хочу придерживаться JSON: API v1.0

Что касается ресурсов, я считаю, что у меня есть "Section" и "ProgressEntry". Каждый ProgressEntry будет иметь непосредственное отношение к секции.

Я бы хотел иметь возможность запрашивать в коллекции, например,

Получить первый предмет в коллекции:

GET /progress-entries?filter[first]

Возвращает:

{
    "data": {
        "type": "progress-entries",
        "id": "progressL",
        "attributes": {
            "sectionId": "sectionG"
        },
        "relationships": {
            "section": {
                "links": {
                    "related": "http://example.com/sections/sectionG"
                }
            }
        }
    },
    "included": [
        {
            "links": {
                "self": "http://example.com/sections/sectionG"
            },
            "type": "sections",
            "id": "sectionG",
            "attributes": {
                "id": "sectionG",
                "title": "Some title"
            }
        }
    ]
}

Получить предыдущее значение ProgressEntry с учетом относительного значения ProgressEntry. Итак, в следующем примере найдите ProgressEntry, атрибут sectionId которого равен «sectionZ», а затем получите предыдущую запись (sectionG). Раньше мне не было ясно, что фильтрация этого основана на атрибутах ProgressEntry:

GET /progress-entries?filter[attributes][sectionId]=sectionZ&filterAction=getPreviousEntry

Возвращает:

{
    "data": {
        "type": "progress-entries",
        "id": "progressL",
        "attributes": {
            "sectionId": "sectionG"
        },
        "relationships": {
            "section": {
                "links": {
                    "related": "http://example.com/sections/sectionG"
                }
            }
        }
    },
    "included": [
        {
            "links": {
                "self": "http://example.com/sections/sectionG"
            },
            "type": "sections",
            "id": "sectionG",
            "attributes": {
                "id": "sectionG",
                "title": "Some title"
            }
        }
    ]
}

1 Ответ

0 голосов
/ 22 июня 2019

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

Ресурс идентифицируется уникальным идентификатором (URI). URI, как правило, не зависит от формата представления, иначе согласование типа содержимого будет бесполезным. json-api - это медиа-тип, который определяет структуру и семантику представлений, которыми обмениваются для конкретного ресурса. Медиа-тип НЕ ДОЛЖЕН налагать какие-либо ограничения на структуру URI ресурса, поскольку он не зависит от него. Невозможно определить тип медиа для использования на основе заданного URI, даже если URI содержит что-то вроде vnd.api+json, поскольку это может быть просто веб-страница, на которой говорится о json: api. Клиент также может запросить application/hal+json вместо application/vnd.api+json для одного и того же URI и получить ту же информацию о состоянии, только что упакованную в другой синтаксис представления, если сервер поддерживает оба формата представления.

Профили, как упомянул jelhan, - это просто механизмы расширения для фактического медиа-типа, которые позволяют специализированному медиа-типу специализироваться через добавление дополнительных ограничений, соглашений или расширений. Такие профили используют URI, аналогичные пространствам имен XML, и эти URI НЕ ДОЛЖНЫ, но ДОЛЖНЫ БЫТЬ обращаемыми, чтобы обеспечить доступ к дальнейшей документации. Не говорится о URI фактического ресурса, отличного от Web Linking , по которому URI могут подсказывать клиенту использовать тип носителя, что я бы не рекомендовал, так как для этого требуется, чтобы клиент имел определенные знание об этой подсказке.

Как уже упоминалось в моих первоначальных комментариях, URI не должны передавать семантику, поскольку существуют отношения ссылок!

Link-отношения

Таким образом, ваш обрисованный в общих чертах ресурс представляет собой набор дополнительных ресурсов, разделов на языке вашего домена. В то время как pagination , как определено в json:api, напрямую не отображается здесь идеально, если у вас не так много разделов, которые вы хотите разделить на несколько страниц, один и тот же концепт можно использовать с использованием стандартизированных отношений ссылок, определенных IANA .

Здесь в какой-то момент сервер может предоставить вам ссылку на ресурс сбора, который может выглядеть следующим образом:

{
  "links": {
    "self": "https://api.acme.org/section-queue",
    "collection": "https://api.acme.org/app-progression",
    ...
  },
  ...
}

Из-за стандартизированного IANA отношения ссылок collection вы знаете, что этот ресурс может содержать коллекцию записей, которые при вызове могут возвращать представление json:api, такое как:

{
  "links": {
    "self": "https://api.acme.org/app-progression",
    "first": "https://api.acme.org/app-progression/sectionG",
    "last": "https://api/acme.org/app-progression/sectionA",
    "current": "https://api.acme.org/app-progression",
    "up": "https://api.acme.org/section-queue",
    "https://api/acme.org/rel/section": "https://api.acme.org/app-progression/sectionG",
    "https://api/acme.org/rel/section": "https://api.acme.org/app-progression/sectionZ",
    "https://api/acme.org/rel/section": "https://api.acme.org/app-progression/sectionA",
    ...
  },
  ...
}

где у вас есть дополнительные ссылки, чтобы перейти вверх или вниз по иерархии или выбрать первый или последний раздел, который закончился. Обратите внимание на последние 3 примера URI, в которых используется механизм типов расширений , определенный в RFC 5988 (Web Linking).

При дальнейшем углублении иерархии вы можете найти ссылки, такие как

{
  "links": {
    "self": "https://api.acme.org/app-progression/sectionZ",
    "first": "https://api.acme.org/app-progression/sectionG",
    "prev": "https://api.acme.org/app-progression/sectionG",
    "next": "https://api.acme.org/app-progression/sectionA",
    "last": "https://api.acme.org/app-progression/sectionA",
    "current": "https://api.acme.org/app-progression/sectionA",
    "up": "https://api.acme.org/app-progression",
    ...
  },
  ...
}

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

Этот подход довольно распространен в Интернете и позволяет серверу легко со временем изменять свою схему URI, поскольку клиенты будут действовать только на имя отношения ссылки и только вызывают URI, не пытаясь вывести из него какую-либо логику. Этот метод также легко использовать для других типов мультимедиа, таких как application/hal+json и т.п., и позволяет кэшировать и повторно использовать каждый из соответствующих ресурсов по умолчанию, что может снизить нагрузку на ваш сервер, в зависимости от количества запросов, которые он имеет дело с.

Обратите внимание, что ни одно слово о фактическом содержании этого раздела еще не было сказано.Это может быть сложное резюме вещей, типичных для разделов, или это может быть просто слово.Мы могли бы классифицировать его и дать ему имя, так как даже простое слово является допустимой целью для ресурса.Кроме того, как упомянул Джим Уэббер , ваши ресурсы, которые вы предоставляете через HTTP (REST), и модель вашего домена не идентичны и обычно не отображаются один на один.

Фильтрация

json:api позволяет семантически группировать параметры, определяя настроенный x-www-form-urlencoded синтаксический анализ .Если согласование типа содержимого используется для согласования json:api в качестве формата представления, вероятность проблем взаимодействия довольно низка, хотя, если такое представление отправляется незапрошенно, анализ таких параметров запроса может завершиться неудачей.

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

Можно использовать изложенные предложения, а также URI в форме

.../application-progress?filter=first
.../application-progress?filter=current
.../application-progress?filter=previous&on=sectionZ

может использоваться вместо.Этот подход должен в дополнение к этому также работать почти на всех клиентах без необходимости изменять их механизм синтаксического анализа в кодировке URL.В дополнение к этому, служебные расходы на управление для возврата URI для других генерируемых медиа-типов также могут быть минимизированы.Обратите внимание, что каждый из URI в приведенном выше примере представляет свой собственный ресурс, и кэш будет хранить ответы на такие ресурсы на основе URI, используемого для получения таких результатов.Такие запросы, как .../application-progress?filter=next&on=sectionG и .../application-progress?filter=previous&on=sectionA, которые получают в основном одинаковые представления, являются двумя отличительными ресурсами, которые будут обрабатываться вашим API два раза, поскольку ответ на первый запрос не может быть повторно использован, поскольку ключ кэша (URI) отличается.Согласно Fielding кеширование является одним из немногих ограничений, которые REST имеет, которые необходимо соблюдать, иначе вы нарушаете его.

Как вы разрабатываете такие URI, здесь полностью зависит от вас.Важно то, как вы учите клиента, когда вызывать такие URI, а когда нет.Здесь опять-таки можно и нужно использовать отношения ссылок.

Резюме

В итоге, какой подход вы предпочитаете, зависит от вас, а также от выбранного вами стиля URI.Клиенты, особенно в среде REST, не заботятся о структуре URI.Они работают с ссылочными отношениями и используют URI только для того, чтобы вызывать его для выполнения своей задачи.Таким образом, серверный API должен помогать клиенту, обучая его тому, что ему нужно знать в текстовой компьютерной игре в 70/80-х годах , как упоминал Джим Уэббер.Полезно думать о модели взаимодействия для проектирования как возможностей и конечного автомата , как объяснил Асбьерн Улсберг.

Хотя вы можете применить фильтрацию к сгруппированным параметрам, предоставленным json:api, такие ссылки могутможет использоваться только внутри представления `json: api´.Если вы скопируете и вставите такую ​​ссылку в браузер или в другой канал, этот клиент может не обработать ее.Поэтому это не будет моим первым выбором, ТБХ.Независимо от того, разрабатываете ли вы разделы как собственный ресурс или просто свойства, которые хотите получить, вы также выбираете здесь.На самом деле мы не знаем, какие разделы есть в вашей доменной модели, IMO звучит как действительный ресурс, хотя он может иметь или не иметь дополнительные свойства.

...