Bayangkan Anda memiliki 2 entitas, Pemain dan Tim , di mana pemain dapat berada di beberapa tim. Dalam model data saya, saya memiliki tabel untuk setiap entitas, dan tabel gabungan untuk mempertahankan hubungan. Hibernate baik-baik saja dalam menangani hal ini, tetapi bagaimana saya bisa mengekspos hubungan ini dalam RESTful API?
Saya bisa memikirkan beberapa cara. Pertama, saya mungkin meminta setiap entitas berisi daftar yang lain, jadi objek Player akan memiliki daftar Tim miliknya, dan setiap objek Tim akan memiliki daftar Pemain yang menjadi miliknya. Jadi untuk menambahkan Pemain ke Tim, Anda hanya akan POST representasi pemain ke titik akhir, sesuatu seperti POST /player
atau POST /team
dengan objek yang sesuai sebagai muatan permintaan. Ini tampaknya yang paling "tenang" bagi saya tetapi terasa agak aneh.
/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'
}
Cara lain yang bisa saya pikirkan untuk melakukan ini adalah dengan mengekspos hubungan sebagai sumber daya dalam dirinya sendiri. Jadi untuk melihat daftar semua pemain di tim tertentu, Anda bisa melakukan GET /playerteam/team/{id}
atau sesuatu seperti itu dan mendapatkan kembali daftar entitas PlayerTeam. Untuk menambahkan pemain ke tim, POST /playerteam
dengan entitas PlayerTeam yang dibangun dengan tepat sebagai muatan.
/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'
]
Apa praktik terbaik untuk ini?
sumber
Saya akan memetakan hubungan tersebut dengan sub-sumber daya, desain / traversal umum akan menjadi:
Dalam istilah Restful ini banyak membantu dalam tidak memikirkan SQL dan bergabung tetapi lebih ke koleksi, sub-koleksi dan traversal.
Beberapa contoh:
Seperti yang Anda lihat, saya tidak menggunakan POST untuk menempatkan pemain ke tim, tetapi PUT, yang menangani hubungan pemain dan tim Anda dengan lebih baik.
sumber
status
sebagai param dalam permintaan PUT? Apakah ada kelemahan dari pendekatan itu?Jawaban yang ada tidak menjelaskan peran konsistensi dan idempotensi - yang memotivasi rekomendasi
UUIDs
/ nomor acak mereka untuk ID danPUT
bukanPOST
.Jika kami mempertimbangkan kasus di mana kami memiliki skenario sederhana seperti " Tambahkan pemain baru ke tim ", kami mengalami masalah konsistensi.
Karena pemain tidak ada, kita perlu:
Namun, jika operasi klien gagal setelah
POST
ke/players
, kami telah membuat pemain yang bukan milik tim:Sekarang kami memiliki duplikat pemain yatim piatu di
/players/5
.Untuk memperbaikinya, kami dapat menulis kode pemulihan khusus yang memeriksa pemain yatim yang cocok dengan beberapa kunci alami (mis
Name
.). Ini adalah kode khusus yang perlu diuji, membutuhkan lebih banyak uang dan waktu, dllUntuk menghindari perlu kode pemulihan kustom, kita dapat menerapkan
PUT
bukannyaPOST
.Dari RFC :
Agar suatu operasi menjadi idempoten, ia harus mengecualikan data eksternal seperti sekuen id yang dihasilkan server. Inilah sebabnya mengapa orang merekomendasikan keduanya
PUT
danUUID
untukId
bersama-sama.Hal ini memungkinkan kami untuk menjalankan kembali baik
/players
PUT
yang/memberships
PUT
tanpa maupun yang konsekuensi:Semuanya baik-baik saja dan kami tidak perlu melakukan apa pun selain mencoba lagi untuk kegagalan parsial.
Ini lebih merupakan tambahan untuk jawaban yang ada tapi saya harap itu menempatkan mereka dalam konteks gambaran yang lebih besar tentang seberapa fleksibel dan dapat diandalkannya ReST.
sumber
23lkrjrqwlej
?Solusi saya lebih suka adalah untuk membuat tiga sumber:
Players
,Teams
danTeamsPlayers
.Jadi, untuk mendapatkan semua pemain tim, pergi saja ke
Teams
sumber daya dan dapatkan semua pemainnya dengan meneleponGET /Teams/{teamId}/Players
.Di sisi lain, untuk mendapatkan semua tim yang dimainkan pemain, dapatkan
Teams
sumber daya di dalamnyaPlayers
. PanggilGET /Players/{playerId}/Teams
.Dan, untuk mendapatkan panggilan hubungan many-to-many
GET /Players/{playerId}/TeamsPlayers
atauGET /Teams/{teamId}/TeamsPlayers
.Perhatikan bahwa, dalam solusi ini, saat Anda menelepon
GET /Players/{playerId}/Teams
, Anda mendapatkan berbagaiTeams
sumber, yang persis sama dengan yang Anda dapatkan saat meneleponGET /Teams/{teamId}
. Kebalikannya mengikuti prinsip yang sama, Anda mendapatkan berbagaiPlayers
sumber daya saat panggilanGET /Teams/{teamId}/Players
.Di salah satu panggilan, tidak ada informasi tentang hubungan yang dikembalikan. Misalnya, tidak ada
contractStartDate
yang dikembalikan, karena sumber daya yang dikembalikan tidak memiliki info tentang hubungan, hanya tentang sumber dayanya sendiri.Untuk menangani hubungan nn, hubungi salah satu
GET /Players/{playerId}/TeamsPlayers
atauGET /Teams/{teamId}/TeamsPlayers
. Panggilan ini mengembalikan sumber daya yang tepatTeamsPlayers
,.Ini
TeamsPlayers
sumber daya telahid
,playerId
,teamId
atribut, serta beberapa orang lain untuk menggambarkan hubungan. Juga, ia memiliki metode yang diperlukan untuk menghadapinya. DAPATKAN, POST, PUT, DELETE dll yang akan mengembalikan, menyertakan, memperbarui, menghapus sumber daya hubungan.Sumber
TeamsPlayers
daya mengimplementasikan beberapa pertanyaan, sepertiGET /TeamsPlayers?player={playerId}
mengembalikan semuaTeamsPlayers
hubungan yang diidentifikasi oleh pemain{playerId}
. Mengikuti ide yang sama, gunakanGET /TeamsPlayers?team={teamId}
untuk mengembalikan semuaTeamsPlayers
yang telah dimainkan di{teamId}
tim. Dalam salah satuGET
panggilan, sumber dayaTeamsPlayers
dikembalikan. Semua data yang terkait dengan hubungan dikembalikan.Saat memanggil
GET /Players/{playerId}/Teams
(atauGET /Teams/{teamId}/Players
), sumber dayaPlayers
(atauTeams
) meneleponTeamsPlayers
untuk mengembalikan tim (atau pemain) terkait menggunakan filter kueri.GET /Players/{playerId}/Teams
bekerja seperti ini:Anda dapat menggunakan algoritma yang sama untuk mendapatkan semua pemain dari tim, saat memanggil
GET /Teams/{teamId}/Players
, tetapi bertukar tim dan pemain.Sumber daya saya akan terlihat seperti ini:
Solusi ini hanya mengandalkan sumber daya REST. Meskipun beberapa panggilan tambahan mungkin diperlukan untuk mendapatkan data dari pemain, tim, atau hubungan mereka, semua metode HTTP mudah diterapkan. POST, PUT, DELETE sederhana dan mudah.
Setiap kali hubungan dibuat, diperbarui atau dihapus, keduanya
Players
danTeams
sumber daya diperbarui secara otomatis.sumber
Saya tahu bahwa ada jawaban yang ditandai sebagai diterima untuk pertanyaan ini, namun, inilah cara kami dapat menyelesaikan masalah yang diajukan sebelumnya:
Katakanlah untuk PUT
Sebagai contoh, semua tindak lanjut akan menghasilkan efek yang sama tanpa perlu disinkronkan karena dilakukan pada satu sumber daya:
sekarang jika kita ingin memperbarui banyak keanggotaan untuk satu tim, kita dapat melakukan hal berikut (dengan validasi yang tepat):
sumber
Saya lebih suka 2
sumber