Imagine que tiene 2 entidades, Jugador y Equipo , donde los jugadores pueden estar en varios equipos. En mi modelo de datos, tengo una tabla para cada entidad y una tabla de unión para mantener las relaciones. Hibernate está bien para manejar esto, pero ¿cómo podría exponer esta relación en una API RESTful?
Puedo pensar en un par de formas. Primero, podría hacer que cada entidad contenga una lista de la otra, por lo que un objeto Jugador tendría una lista de Equipos a los que pertenece, y cada objeto Equipo tendría una lista de Jugadores que pertenecen a él. Entonces, para agregar un jugador a un equipo, simplemente PUBLICA la representación del jugador en un punto final, algo así como POST /player
o POST /team
con el objeto apropiado como carga útil de la solicitud. Esto me parece el más "RESTful", pero se siente un poco raro.
/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'
}
La otra forma en que se me ocurre hacer esto sería exponer la relación como un recurso por derecho propio. Entonces, para ver una lista de todos los jugadores en un equipo determinado, puede hacer un GET /playerteam/team/{id}
o algo así y recuperar una lista de entidades de PlayerTeam. Para agregar un jugador a un equipo, POST /playerteam
con una entidad PlayerTeam adecuadamente construida como 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'
]
¿Cuál es la mejor práctica para esto?
fuente
Mapearía tal relación con los sub-recursos, el diseño / recorrido general sería:
En términos de descanso, ayuda mucho no pensar en SQL y se une, pero más en colecciones, subcolecciones y recorrido.
Algunos ejemplos:
Como puede ver, no uso POST para colocar jugadores en equipos, sino PUT, que maneja mejor su relación n: n de jugadores y equipos.
fuente
status
como parámetro en la solicitud PUT. ¿Hay un inconveniente en ese enfoque?Las respuestas existentes no explican los roles de consistencia e idempotencia, lo que motiva sus recomendaciones de
UUIDs
/ números aleatorios para ID y enPUT
lugar dePOST
.Si consideramos el caso en el que tenemos un escenario simple como " Agregar un nuevo jugador a un equipo ", nos encontramos con problemas de coherencia.
Debido a que el jugador no existe, necesitamos:
Sin embargo, si la operación del cliente falla después del
POST
to/players
, hemos creado un jugador que no pertenece a un equipo:Ahora tenemos un jugador duplicado huérfano
/players/5
.Para solucionar esto, podríamos escribir un código de recuperación personalizado que verifique si hay jugadores huérfanos que coinciden con alguna clave natural (por ejemplo
Name
). Este es un código personalizado que necesita ser probado, cuesta más dinero y tiempo, etc.Para evitar la necesidad de un código de recuperación personalizado, podemos implementarlo en
PUT
lugar dePOST
.Desde el RFC :
Para que una operación sea idempotente, debe excluir datos externos como secuencias de identificación generadas por el servidor. Esta es la razón por la cual las personas recomiendan ambos
PUT
yUUID
s paraId
s juntos.Esto nos permite volver a ejecutar tanto el
/players
PUT
y el/memberships
PUT
sin consecuencias:Todo está bien y no tuvimos que hacer nada más que volver a intentarlo por fallas parciales.
Esto es más una adición a las respuestas existentes, pero espero que las ponga en el contexto de la imagen más amplia de cuán flexible y confiable puede ser ReST.
fuente
23lkrjrqwlej
?Mi solución preferida es la creación de tres recursos:
Players
,Teams
yTeamsPlayers
.Entonces, para obtener todos los jugadores de un equipo, solo vaya al
Teams
recurso y obtenga todos sus jugadores llamandoGET /Teams/{teamId}/Players
.Por otro lado, para obtener todos los equipos que un jugador ha jugado, obtenga el
Teams
recurso dentro delPlayers
. LlamadaGET /Players/{playerId}/Teams
.Y, para obtener la llamada de relación de muchos a muchos
GET /Players/{playerId}/TeamsPlayers
oGET /Teams/{teamId}/TeamsPlayers
.Tenga en cuenta que, en esta solución, cuando llama
GET /Players/{playerId}/Teams
, obtiene una variedad deTeams
recursos, que es exactamente el mismo recurso que obtiene cuando llamaGET /Teams/{teamId}
. Lo contrario sigue el mismo principio, obtienes una variedad dePlayers
recursos cuando llamasGET /Teams/{teamId}/Players
.En cualquiera de las llamadas, no se devuelve información sobre la relación. Por ejemplo, no
contractStartDate
se devuelve, porque el recurso devuelto no tiene información sobre la relación, solo sobre su propio recurso.Para lidiar con la relación nn, llame a
GET /Players/{playerId}/TeamsPlayers
oGET /Teams/{teamId}/TeamsPlayers
. Estas llamadas devuelven la exactitud de recursos,TeamsPlayers
.Este
TeamsPlayers
recurso haid
,playerId
,teamId
atributos, así como algunos otros para describir la relación. Además, tiene los métodos necesarios para tratar con ellos. GET, POST, PUT, DELETE, etc. que devolverá, incluirá, actualizará, eliminará el recurso de relación.El
TeamsPlayers
recurso implementa algunas consultas, comoGET /TeamsPlayers?player={playerId}
devolver todas lasTeamsPlayers
relaciones que tiene el jugador identificado{playerId}
. Siguiendo la misma idea, useGET /TeamsPlayers?team={teamId}
para devolver todo loTeamsPlayers
que ha jugado en el{teamId}
equipo. En cualquierGET
llamada,TeamsPlayers
se devuelve el recurso . Se devuelven todos los datos relacionados con la relación.Al llamar
GET /Players/{playerId}/Teams
(oGET /Teams/{teamId}/Players
), el recursoPlayers
(oTeams
) llamaTeamsPlayers
para devolver los equipos (o jugadores) relacionados mediante un filtro de consulta.GET /Players/{playerId}/Teams
funciona así:Puede usar el mismo algoritmo para obtener todos los jugadores de un equipo, al llamar
GET /Teams/{teamId}/Players
, pero intercambiando equipos y jugadores.Mis recursos se verían así:
Esta solución se basa únicamente en recursos REST. Aunque algunas llamadas adicionales pueden ser necesarias para obtener datos de jugadores, equipos o su relación, todos los métodos HTTP se implementan fácilmente. POST, PUT, DELETE son simples y directos.
Cada vez que se crea, actualiza o elimina una relación, ambos
Players
y losTeams
recursos se actualizan automáticamente.fuente
Sé que hay una respuesta marcada como aceptada para esta pregunta, sin embargo, así es como podemos resolver los problemas planteados anteriormente:
Digamos por PUT
Como ejemplo, los siguientes resultados tendrán el mismo efecto sin necesidad de sincronización porque se realizan en un solo recurso:
ahora si queremos actualizar varias membresías para un equipo, podríamos hacer lo siguiente (con las validaciones adecuadas):
fuente
Prefiero 2
fuente