web-dev-qa-db-ja.com

ネストされたREST URLと親ID、どちらがより良いデザインですか?

さて、2つのリソースがあります:AlbumSong。ここにAPIがあります:

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

嫌いな曲があることは知っています。たとえば、Susyと呼ばれます。 searchアクションをどこに置くべきですか?

別の質問。さて、今それはより現実的です。アルバム1を開き、すべての曲をロードします。 JSオブジェクトを作成します。それぞれが曲のデータを保持し、removeupdateのようないくつかのメソッドがあります。

SongオブジェクトにはID、名前などが含まれていますが、クエリによって曲のリストを取得するため、それぞれの親IDを返すのはあまり良くないため、どの親に属しているかはわかりません。私が間違っている?

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

  1. 親IDをオプションにします-get-parameterとして。この方法は現在使用していますが、醜い方法だと感じています。

    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
    

どちらが良いですか?それとも私は馬鹿ですか?アドバイスを投げてね!

21
dt0xff

検索アクションをどこに置くべきですか?

GET /search/:text内。これにより、一致を含むJSON配列が返されます。すべての一致には、それが属するアルバムが含まれます。クライアントはトラック自体ではなくアルバム全体に関心があるため、これは理にかなっています(名前を覚えているのと同じアルバムにあると思われる曲を検索しているとします)。

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

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

どちらが良いですか?

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

それとも私は馬鹿ですか?

特に正しい答えが1つもないため、適切なURIを選択するのは簡単なことではありません。 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

は大丈夫です。

31