REST и многие ко многим

Я основываю свой вопрос на Как обращаться с отношения «многие ко многим» в RESTful API?, но хотите продолжить с принятого ответа.

Предположим, у нас есть отношение «многие ко многим» между игроками и командами (точно так же, как в вопросе, упомянутом выше).

Насколько я понимаю, есть несколько вариантов моделирования этого с помощью ресурсов REST:

Полезная нагрузка содержит ссылки на связанные ресурсы

GET /players/john

урожаи

{
    "name": "John",
    "_links": [
        {
            "rel": "team",
            "href": "/teams/1"
        },
        {
            "rel": "team",
            "href": "/teams/4"
        }
    ]
}

и

GET /teams/1

урожаи

{
    "name": "Team 1",
    "_links": [
        {
            "rel": "player",
            "href": "/players/john"
        },
        ...
    ]
}

Это заставляет меня обновлять ресурс игрока, когда я просто хочу добавить игрока в команду. Кроме того, когда я добавляю игрока в команду, используя ресурс игрока, соответствующий ресурс команды автоматически обновляется. Согласно Как справиться со многими отношения "ко многим" в RESTful API?:

вы не хотите, чтобы альтернативный URL-адрес /players/5/teams/ оставался в кеше

В этом случае team/1 может оставаться в кеше, когда я обновляю игрока «Джон», чтобы удалить из него команду «Team 1»!

Отношения моделируются как еще один ресурс

GET /teams/1

урожаи

{
    "name": "Team 1",
}

и

GET /players/john

урожаи

{
    "name": "John",
}

Ну наконец то,

GET /relationships

урожаи

    [
    {
        "_links": [
            {
                "rel": "player",
                "href": "/players/john"
            },
            {
                "rel": "team",
                "href": "/teams/1"
            }
        ]
    },

    ...
]

Таким образом, я могу создавать и удалять отношения, не затрагивая как ресурсы игрока, так и ресурсы команды. Но когда я удаляю /players/john, должны ли совпадающие отношения также автоматически удаляться? В этом случае нарушается то же правило, что и выше. Если это не так, нам нужно вручную удалить эти отношения, что требует большой работы, которой я не хочу обременять потребителей моего API.

Кроме того, если мы хотим обновить команды, в которых состоит определенный игрок «Джон», нам нужно удалить некоторые отношения и добавить другие. Мы открываемся для конфликтов слияния (и условий гонки), когда кто-то еще редактирует игрока «Джон» или команду «Команда 1».

Каждая сторона отношений получает свой собственный объект отношений

GET /teams/1/players

получается что-то вроде

{
    "_links": [
        {
            "rel": "player",
            "href": "/players/john"
        },
        ...
    ]
}

и

GET /players/john/teams

что-то типа

{
    "_links": [
        {
            "rel": "team",
            "href": "/teams/1"
        },
        ...
    ]
}

Но добавление или удаление может по-прежнему влиять на ресурс, расположенный по другому URL-адресу (который не имеет общего корневого элемента).

Мои вопросы

Есть ли проблемы, о которых я упоминал в обоих случаях?

Какой из обоих подходов «предпочтительнее» или более чистый REST?

Насколько серьезно я должен относиться к ограничениям, упомянутым в Как обрабатывать отношения "многие ко многим" в RESTful API?:

вы не хотите, чтобы альтернативный URL-адрес /players/5/teams/ оставался в кеше

Заранее спасибо!


person Werner de Groot    schedule 12.03.2015    source источник
comment
Это вынуждает меня обновлять ресурс игрока, когда я просто хочу добавить игрока в команду. почему это плохо?   -  person Tim    schedule 12.03.2015
comment
Например, мне нужно знать все ДРУГИЕ команды, в которых находится игрок, чтобы обновить игрока до нужного состояния (имея ВСЕ правильные отношения с командами). Кроме того, я хочу разрешить игрокам менять команды, но я не хочу позволять игрокам менять свои имена. Это входит в обязанности администратора. Оба используют один и тот же ресурс, и мне нужно проверить роли и сравнить их с запрошенными изменениями, а не просто запретить PUT или POST для ресурса для некоторых пользователей.   -  person Werner de Groot    schedule 12.03.2015
comment
Мне нужно знать все ДРУГИЕ команды, в которых состоит игрок почему? вы можете просто добавить команду в player.teams   -  person Tim    schedule 12.03.2015
comment
Как это сработает? Могли бы вы тогда сказать, что предпочитаете первый подход?   -  person Werner de Groot    schedule 12.03.2015
comment
Вы должны ПОСТАВИТЬ пользователя и добавить новую команду в список команд пользователя.   -  person Tim    schedule 12.03.2015
comment
Не могли бы вы объяснить на паре примеров, что вы имеете в виду? Где находится ресурс «Команда 1», где находится ресурс «Джон»? Как выглядят ответы? Как получить все команды определенного игрока и наоборот? Заранее спасибо!   -  person Werner de Groot    schedule 13.03.2015


Ответы (1)


У вас может быть следующее

Команда

GET /teams/dream

{
    "_links": {
        "self": {
            "href": "/teams/dream"
        }
        "players": {
            "href": "/players?team=dream"
        }
    },
    "name": "Dream"
}

Игрок

GET /player/john

{
    "_links": {
        "self": {
            "href": "/player/john"
        },
        "teams": {
            "href": "/teams?player=john"
        },
    },
    "name": "John",
}

команды Джона

GET /teams?player=john

{
    "_links": {
    },
    "items": [
        {
            "href": "/teams/a-team"
        }
    ],
}

Добавление Джона в команду мечты (например, с использованием патча json) (строка запроса в сообщении патча... и т. д., хотя и редко, но действительна)

PATCH /teams?player=john

[{
    "op": "add",
    "path": "/-",
    "value": {
        "href": "/team/dream"
    }
}]

Получить команды Джона

GET /teams?player=john

{
    "_links": {
    },
    "items": [
        {
            "href": "/teams/a-team"
        },
        {
            "href": "/teams/dream"
        }
    ]
}

Джон покидает команду A:

PATCH /teams?player=john

[{
    "op": "remove",
    "path": "/0"
}]
person redben    schedule 22.03.2015
comment
Спасибо, что нашли время ответить на мой вопрос. Есть ли причина, по которой вы используете удаление и добавление атрибута op вместо использования DELETE и PATCH? А как УДАЛИТЬ все отношения Джона? Через /teams?user=john? - person Werner de Groot; 23.03.2015
comment
DELETE /teams?player=john Для патча вы можете использовать любую структуру, которую хотите, но есть попытки сделать что-то стандартное для этого. См. jsonpatch.com. - person redben; 23.03.2015
comment
Спасибо за ваш ответ. Я не знал о патче JSON, но похоже, что он может решить многие мои проблемы! - person Werner de Groot; 27.03.2015
comment
Я бы (и на практике) использовал фактический путь к ресурсу (т.е. /teams/a-team) в операции удаления. Вы не хотели бы использовать индекс. Даже если порядок был гарантирован (редко в коллекции), другой пользователь мог удалить более ранний индекс между моментами GET и DELETE. В конечном итоге вы затронете не ту запись. - person claytond; 23.03.2018