ネストされたREST URLと親ID、どちらが優れた設計ですか?


19

さて、2つのリソースがAlbumありSongます:と。APIは次のとおりです。

GET,POST /albums
GET,POST /albums/:albumId
GET,POST /albums/:albumId/songs
GET,POST /albums/:albumId/songs/:songId

私たちはいくつかの歌が嫌いであることを知っていSusyます。たとえば、と呼ばれます。どこにsearch行動を起こすべきか?

別の質問。さて、今ではもっとリアルになっています。アルバム1を開き、すべての曲を読み込みます。JSオブジェクトを作成します。それぞれが曲データを保持しremove、次のようなメソッドはほとんどありませんupdate

曲オブジェクトにはID、名前、およびものがありますが、どの親に属しているかについての手がかりはありません。これは、クエリによって曲のリストを取得するためです。私が間違っている?

だから、私はいくつかの解決策を見ますが、私は本当に確信がありません。

  1. 親IDをオプションにします-get-parameterとして。私は現在このアプローチを使用していますが、butいアプローチだと感じています。

    List,Create /songs?album=albumId
    Update,Delete /songs/:songId
    Get /songs/?name=susy # also, solution for first question
    
  2. ハイブリッド。OPTIONSメタデータを取得するためのクエリを実行するためにアルバムIDが必要なため、今では便利です。

    List,Create /album/:albumId/songs
    Update,Delete /songs/:songId
    POST /songs/search # also, solution for first question
    
  3. 各リソースインスタンスで完全なURLを返します。APIは同じですが、次のような曲を取得します。

    id: 5
    name: 'Elegy'
    url: /albums/2/songs/5
    

    このアプローチはHATEOASと呼ばれると聞きました。

  4. だから...親IDを提供するには

    id: 5
    name: 'Elegy'
    albumId: 2
    

どちらが良いですか?または多分私は愚かですか?アドバイスを投げましょう!

回答:


31

検索アクションを配置する場所

GET /search/:text。これにより、一致を含むJSON配列が返され、すべての一致には、所属するアルバムが含まれます。これは理にかなっています。クライアントはトラック自体ではなく、アルバム全体に興味があるかもしれないからです(名前を覚えているのと同じアルバムにある曲を探していると想像してください)。

それぞれの親IDを返すのはあまり良くありません。私が間違っている?

個々のトラックにアルバムを含めることができます。これにより、アルバムまたは検索(ここではアルバムなし)のいずれかでトラックを取得できる場合、トラック表現が均一になります。

どちらが良いですか?

前述のように、アルバムを含めることは理にかなっています。3番目のポイント(相対URIを使用)は、場合によっては興味深いものになりますが(URIの形成方法について考える必要はありません)、明示的にアルバムを提供しないという欠点があります。4番目のポイントはこれを修正します。応答に相対URIを含める利点がある場合は、ポイント3と4を組み合わせることができます。

または多分私は愚かですか?

適切なURIを選択することは簡単な作業ではありません。特に、正しい答えが1つもないためです。APIと同時にクライアントを開発する場合、APIの使用方法を視覚化するのに役立ちます。そうは言っても、他の人は、APIの開発時に考えていなかった他の使用法を好むかもしれません。

問題となる可能性のある側面は、データを内部的に整理する方法、つまり階層の使用方法です。あなたのコメントから、あなたはGET /artist/1/album/10/song/3/comment/23非常にツリー指向のビジョンを示すに対する応答を何に含めるべきか疑問に思っています。これにより、後でシステムを拡張するときにいくつかの問題が発生する可能性があります。例えば:

  • 曲にアルバムがない場合はどうなりますか?
  • アルバムに複数のアーティストがいる場合はどうなりますか?
  • アルバムにコメントできる機能を追加したい場合はどうしますか?
  • コメントのコメントがある場合はどうなりますか?

これは本質的に私のブログで説明した問題です。ツリー表現には多くの場合に効果的に使用するには制限が多すぎます。

階層を破壊するとどうなりますか?どれどれ。

  1. GET /albums/:albumIdアルバムに関するメタ情報(公開された年やアルバムカバーを示すJPEGのURIなど)とトラックの配列を含むJSONを返します。例えば:

    GET /albums/151
    {
        "id": 151,
        "gid": "dbd3cec7-b927-423f-894b-742c4c7b54ce",
        "name": "Yellow Submarine",
        "year": 1969,
        "genre": "Psychedelic rock",
        "artists": ["John Lennon", "Paul McCartney", ...],
        "tracks": [
            {
                "id": 90224,
                "title": "Yellow Submarine",
                "length": "2:40"
            },
            {
                "id": 83192,
                "title": "Only a Northern Song",
                "length": "3:24"
            }
            ...
        ]
    }

    たとえば、なぜ各トラックの長さを含めるのですか?アルバムを表示しているクライアントは、タイトルごとにトラックをリストすることに興味があるかもしれないが、ほとんどのクライアントがそうするように、各トラックの長さも表示することを想像するからです。一方、このレベルではこの情報は必要ないと判断したため、すべてのトラックの作曲者またはアーティストを表示しない場合があります。明らかに、選択は異なる場合があります。

  2. GET /tracks/:trackId特定のトラックに関する情報を返します。階層がなくなったので、アルバムやアーティストを推測する必要はありません。本当に知っておく必要があるのは、トラック自体の識別子だけです。

    それともそうでないのでしょうか?名前で指定できる場合はどうしますGET /tracks/:trackNameか?

    GET /tracks/Only%20a%20Northern%20Song
    {
        "id": 83192,
        "gid": "8d9c4311-9d7b-40a4-8aeb-4fe96247fe2b",
        "title": "Only a Northern Song",
        "writers": ["George Harrison"],
        "artists": ["John Lennon", "Paul McCartney", "Ringo Starr"],
        "length": "3:24",
        "record-date": 1967,
        "albums": [151, 164],
        "soundtrack": {
            "uri": "http://audio.example.com/tracks/static/83192.mp3",
            "alias": "Beatles - Only a Northern Song.mp3",
            "length-bytes": 3524667,
            "allow-streaming": true,
            "allow-download": false
        }
    }

    を詳しく見てみましょうalbums。何が見えますか?そう、1つではなく、2つのアルバム。階層がある場合、それを行うことはできません(レコードを複製しない限り)。

  3. GET /comments/:objectGid。応答にいGUIDを見つけた可能性があります。これらのGUIDを使用すると、アルバム、アーティスト、またはトラックに適用できるタスクを実行するために、データベース全体でエンティティを識別できます。コメントなど。

    GET /comments/8d9c4311-9d7b-40a4-8aeb-4fe96247fe2b
    [
        {
            "author": {
                "id": 509931,
                "display-name": "Arseni Mourzenko"
            },
            "text": "What a great song! (And I'm proud of the usefulness of my comment)",
            "concerned-object": "/tracks/83192"
        }
    ]

    コメントは関連するオブジェクトを参照し、そのコンテキスト外のコメントにアクセスするとき(たとえば、を通じて最新のコメントをモデレートするとき)にアクセスできるようにしますGET /comments/latest

これは、APIで階層の形式を避ける必要があるという意味ではないことに注意してください。理にかなっている場合があります。経験則として:

  • リソースがその親リソースのコンテキスト外で意味をなさない場合は、階層を使用します。

  • リソースが(1)単独で、または(2)異なるタイプの親リソースのコンテキストで、または(3)複数の親を持つことができる場合、階層は使用しないでください。

たとえば、ファイルの行はファイルのコンテキスト外では意味をなさないため、次のようになります。

GET /file/:fileId

そして:

GET /file/:fileId/line/:lineIndex

は大丈夫です。


ええ、検索から、私も完全なアルバム情報を返すことができます、それは別のリソースを作るでしょう- SongSearchResult、それは大丈夫だと思います。しかし、URLについてはどうでしょうか?parentID各オブジェクトを提供し、GETパラメーターまたはURLの通常の部分として使用する必要がありますか?depth> 2の場合はどうなりますか?/artist/1/album/10/song/3/comment/23- commentオブジェクト、アーティスト、アルバム、曲のすべてのIDをオブジェクトに提供するのは正気ではありませんが、それは進むべき道であると聞いたのですが、見た目は悪くありませんか?!
dt0xff

@ dt0xff:回答を編集しました。これで、深度をどのように回避できるかについて明確なイメージが得られるはずです。
アルセニムルゼンコ

ええ、URLで親に追加することなく、各リソース(行などの機能的なものを除く)のエントリポイントを実装する方がはるかに簡単であることは明らかです。ありがとう、あなたは私の選択が正しかったと確信し、「共通のアプローチ」(実際には多くの入れ子になったものrestangularが構築されています)はあまり良くありません。
dt0xff

素晴らしい答え。しかし、私はいくつかの異論があります。「アルバムに複数のアーティストがいる場合はどうなりますか?」バイナリ関係URI->リソースは完全に一意(多対1)であるため、異なるURIで同じリソースを識別できます。URIはそう/artists/foo/albums/qux/artists/bar/albums/qux完全に同じアルバムのリソースを識別することができます。言い換えると、URIのパスコンポーネントはグラフ階層を表し、必ずしもツリー階層ではないため、カテゴリだけでなくタグも表すのに適しています。
マゲロ

1
…「これは本質的に私のブログで説明した問題です。ツリー表現には多くの場合に効果的に使用するには制限が多すぎます。」いいえ、これは問題ではありません。「アルバムにコメントを付けられる機能を追加したい場合はどうしますか?」それはどちらかの問題ではありません:/artists/foo/albums/qux/comments/7。「コメントのコメントがあるとしたら?」同様に:/artists/foo/albums/qux/song/5/comments/2/comments/8
マゲロ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.