Imagine que você tem 2 entidades, Jogador e Equipe , onde os jogadores podem estar em várias equipes. No meu modelo de dados, tenho uma tabela para cada entidade e uma tabela de junção para manter os relacionamentos. O Hibernate é bom em lidar com isso, mas como posso expor esse relacionamento em uma API RESTful?
Eu posso pensar em algumas maneiras. Primeiro, posso fazer com que cada entidade contenha uma lista da outra, para que um objeto Player tenha uma lista de equipes às quais pertence e cada objeto Team tenha uma lista de jogadores que pertencem a ela. Então, para adicionar um jogador a uma equipe, você apenas colocaria a representação do jogador em um endpoint, algo como POST /player
ou POST /team
com o objeto apropriado como a carga útil da solicitação. Isso parece o mais "RESTful" para mim, mas parece um pouco estranho.
/api/team/0:
{
name: 'Boston Celtics',
logo: '/img/Celtics.png',
players: [
'/api/player/20',
'/api/player/5',
'/api/player/34'
]
}
/api/player/20:
{
pk: 20,
name: 'Ray Allen',
birth: '1975-07-20T02:00:00Z',
team: '/api/team/0'
}
A outra maneira que posso pensar em fazer isso seria expor o relacionamento como um recurso por si só. Portanto, para ver uma lista de todos os jogadores de um determinado time, você pode fazer um GET /playerteam/team/{id}
ou algo assim e recuperar uma lista de entidades do PlayerTeam. Para adicionar um jogador a uma equipe, POST /playerteam
com uma entidade PlayerTeam criada adequadamente como a carga útil.
/api/team/0:
{
name: 'Boston Celtics',
logo: '/img/Celtics.png'
}
/api/player/20:
{
pk: 20,
name: 'Ray Allen',
birth: '1975-07-20T02:00:00Z',
team: '/api/team/0'
}
/api/player/team/0/:
[
'/api/player/20',
'/api/player/5',
'/api/player/34'
]
Qual é a melhor prática para isso?
fonte
Gostaria de mapear essa relação com sub-recursos, o design geral / travessia seria:
Em termos repousantes, ajuda muito a não pensar em SQL e une-se, mas mais em coleções, sub-coleções e travessias.
Alguns exemplos:
Como você vê, não uso o POST para colocar jogadores nas equipes, mas o PUT, que lida melhor com o seu relacionamento n: n de jogadores e equipes.
fonte
status
como param na solicitação PUT? Existe uma desvantagem nessa abordagem?As respostas existentes não explicam os papéis de consistência e idempotência - que motivam suas recomendações de
UUIDs
/ números aleatórios para IDs ePUT
nãoPOST
.Se considerarmos o caso em que temos um cenário simples como " Adicionar um novo jogador a um time ", encontraremos problemas de consistência.
Como o player não existe, precisamos:
No entanto, se a operação do cliente falhar após o
POST
que/players
, criamos um jogador que não pertence a uma equipe:Agora temos um reprodutor duplicado órfão
/players/5
.Para corrigir isso, podemos escrever um código de recuperação personalizado que verifique se há jogadores órfãos que correspondem a alguma chave natural (por exemplo
Name
). Este é um código personalizado que precisa ser testado, custa mais dinheiro e tempo, etc.Para evitar a necessidade de código de recuperação personalizado, podemos implementar em
PUT
vez dePOST
.Do RFC :
Para que uma operação seja idempotente, ela precisa excluir dados externos, como sequências de identificação geradas pelo servidor. É por isso que as pessoas recomendam es
PUT
e es juntos.UUID
Id
Isso nos permite executar novamente
/players
PUT
as/memberships
PUT
consequências sem e com:Está tudo bem e não precisamos fazer nada além de tentar novamente por falhas parciais.
Este é mais um adendo às respostas existentes, mas espero que as coloque no contexto de uma imagem maior de quão flexível e confiável o ReST pode ser.
fonte
23lkrjrqwlej
?Minha solução preferida é a criação de três recursos:
Players
,Teams
eTeamsPlayers
.Portanto, para obter todos os jogadores de uma equipe, basta acessar os
Teams
recursos e obter todos os jogadores ligandoGET /Teams/{teamId}/Players
.Por outro lado, para obter todos os times que um jogador jogou, obtenha o
Teams
recurso dentro doPlayers
. LigueGET /Players/{playerId}/Teams
.E, para receber a ligação de relacionamento muitos-para-muitos
GET /Players/{playerId}/TeamsPlayers
ouGET /Teams/{teamId}/TeamsPlayers
.Observe que, nesta solução, quando você liga
GET /Players/{playerId}/Teams
, você obtém uma matriz deTeams
recursos, que é exatamente o mesmo recurso que você recebe quando ligaGET /Teams/{teamId}
. O inverso segue o mesmo princípio, você obtém uma matriz dePlayers
recursos quando chamaGET /Teams/{teamId}/Players
.Nas duas chamadas, nenhuma informação sobre o relacionamento é retornada. Por exemplo, no
contractStartDate
é retornado, porque o recurso retornado não possui informações sobre o relacionamento, apenas sobre seu próprio recurso.Para lidar com o relacionamento nn, ligue para
GET /Players/{playerId}/TeamsPlayers
ouGET /Teams/{teamId}/TeamsPlayers
. Essas chamadas retornam exatamente o recursoTeamsPlayers
,.Este
TeamsPlayers
recurso temid
,playerId
,teamId
atributos, bem como alguns outros para descrever a relação. Além disso, possui os métodos necessários para lidar com eles. GET, POST, PUT, DELETE etc que retornarão, incluirão, atualizarão, removerão o recurso de relacionamento.O
TeamsPlayers
recurso implementa algumas consultas, comoGET /TeamsPlayers?player={playerId}
retornar todos osTeamsPlayers
relacionamentos identificados pelo jogador{playerId}
. Seguindo a mesma idéia, useGET /TeamsPlayers?team={teamId}
para retornar tudo oTeamsPlayers
que jogou no{teamId}
time. Em qualquerGET
chamada, o recursoTeamsPlayers
é retornado. Todos os dados relacionados ao relacionamento são retornados.Ao chamar
GET /Players/{playerId}/Teams
(ouGET /Teams/{teamId}/Players
), o recursoPlayers
(ouTeams
) chamaTeamsPlayers
para retornar as equipes (ou jogadores) relacionadas usando um filtro de consulta.GET /Players/{playerId}/Teams
funciona assim:Você pode usar o mesmo algoritmo para obter todos os jogadores de uma equipe ao fazer uma chamada
GET /Teams/{teamId}/Players
, mas trocar equipes e jogadores.Meus recursos ficariam assim:
Esta solução depende apenas dos recursos REST. Embora algumas chamadas extras possam ser necessárias para obter dados de jogadores, equipes ou seu relacionamento, todos os métodos HTTP são facilmente implementados. POST, PUT, DELETE são simples e diretos.
Sempre que um relacionamento é criado, atualizado ou excluído, tanto
Players
eTeams
recursos são atualizados automaticamente.fonte
Sei que há uma resposta marcada como aceita para esta pergunta, no entanto, eis como podemos resolver os problemas levantados anteriormente:
Digamos que PUT
Como exemplo, todos os itens a seguir resultarão no mesmo efeito sem a necessidade de sincronização, porque são feitos em um único recurso:
agora, se quisermos atualizar várias associações para uma equipe, poderíamos fazer o seguinte (com validações apropriadas):
fonte
Eu prefiro 2
fonte