Các url REST lồng nhau và id cha, thiết kế nào tốt hơn?


19

Được rồi, chúng tôi có hai tài nguyên: AlbumSong. Đây là API:

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

Chúng tôi biết rằng chúng tôi ghét một số bài hát, nó được gọi là Susy, ví dụ. Chúng ta nên đặt searchhành động ở đâu?

Câu hỏi khác. Được rồi, bây giờ nó là một thực tế hơn. Chúng tôi mở album 1 và tải tất cả các bài hát. Chúng tôi tạo ra các đối tượng JS, từng nắm giữ dữ liệu âm nhạc và có vài phương pháp như vậy: remove, update.

Đối tượng bài hát có ID, tên và nội dung, nhưng không biết nó thuộc về cha mẹ nào, bởi vì chúng tôi truy xuất danh sách các bài hát theo truy vấn và sẽ không tốt khi trả lại id cha với mỗi cái như vậy. Tôi có lầm không?

Vì vậy, tôi thấy một vài giải pháp, nhưng tôi không chắc lắm.

  1. Đặt id cha tùy chọn - làm tham số get. Cách tiếp cận này tôi sử dụng hiện tại, nhưng tôi cảm thấy nó là một cách xấu xí.

    List,Create /songs?album=albumId
    Update,Delete /songs/:songId
    Get /songs/?name=susy # also, solution for first question
    
  2. Hỗn hợp. Bây giờ thật tiện lợi vì chúng tôi cần id album để thực hiện OPTIONStruy vấn để lấy siêu dữ liệu.

    List,Create /album/:albumId/songs
    Update,Delete /songs/:songId
    POST /songs/search # also, solution for first question
    
  3. Trả lại url đầy đủ với mỗi phiên bản tài nguyên. API giống nhau, nhưng chúng tôi sẽ nhận được các bài hát như thế này:

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

    Tôi nghe nói phương pháp này được gọi là HATEOAS.

  4. Vì vậy, ... Để cung cấp ID cha

    id: 5
    name: 'Elegy'
    albumId: 2
    

Cái nào tốt hơn? Hay có lẽ tôi bị câm? Ném một số lời khuyên, các bạn!

Câu trả lời:


31

Chúng ta nên đặt hành động tìm kiếm ở đâu?

Trong GET /search/:text. Điều này sẽ trả về một mảng JSON chứa các trận đấu, mọi trận đấu có chứa album mà nó thuộc về. Điều này có ý nghĩa, bởi vì khách hàng có thể không quan tâm đến bản nhạc, mà toàn bộ album (hãy tưởng tượng rằng bạn đang tìm kiếm một bài hát mà bạn tin là trong cùng một album với bài bạn nhớ tên).

Sẽ không tốt nếu trả lại id cha với mỗi cái như vậy. Tôi có lầm không?

Các bản nhạc riêng lẻ có thể chứa album. Điều này sẽ đảm bảo rằng đại diện theo dõi là thống nhất nếu bạn có thể nhận được một bản nhạc thông qua một album hoặc thông qua tìm kiếm (không có album ở đây).

Cái nào tốt hơn?

Như đã nêu trước đây, bao gồm cả album có ý nghĩa. Mặc dù điểm thứ ba (với URI tương đối) có thể thú vị trong một số trường hợp (bạn không phải suy nghĩ về cách hình thành URI), nhưng nó có một nhược điểm là không cung cấp rõ ràng album. Điểm thứ tư sửa lỗi này. Nếu bạn thấy lợi ích của việc có URI tương đối trong phản hồi, bạn có thể kết hợp điểm 3 và 4.

Hay có lẽ tôi bị câm?

Chọn URI tốt không phải là một nhiệm vụ dễ dàng, đặc biệt là vì không có câu trả lời đúng duy nhất. Nếu bạn phát triển ứng dụng khách cùng lúc với API, nó có thể giúp bạn hình dung rõ hơn cách sử dụng API. Điều này đang được nói, những người khác sau đó có thể thích các cách sử dụng khác mà bạn không nghĩ đến khi phát triển API.

Một khía cạnh có thể có vấn đề là cách bạn tổ chức dữ liệu trong nội bộ, tức là việc sử dụng hệ thống phân cấp. Từ nhận xét của bạn, bạn đang tự hỏi những gì nên chứa một phản ứng GET /artist/1/album/10/song/3/comment/23, điều này cho thấy một tầm nhìn rất hướng về cây. Điều này có thể dẫn đến một vài vấn đề khi mở rộng hệ thống sau này. Ví dụ:

  • Nếu một bài hát không có album thì sao?
  • Điều gì nếu một album có nhiều nghệ sĩ?
  • Điều gì nếu bạn muốn thêm một tính năng mà có thể nhận xét album?
  • Điều gì nếu có ý kiến ​​của ý kiến?
  • v.v.

Đây thực chất là vấn đề tôi đã giải thích trong blog của mình : một đại diện cho cây có quá nhiều hạn chế để được sử dụng hiệu quả trong nhiều trường hợp.

Điều gì xảy ra nếu bạn phá hủy hệ thống phân cấp? Hãy xem nào.

  1. GET /albums/:albumIdtrả về một JSON chứa thông tin meta về album (chẳng hạn như năm khi nó được xuất bản hoặc URI của JPEG hiển thị bìa album) và một loạt các bản nhạc. Ví dụ:

    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"
            }
            ...
        ]
    }

    Tại sao tôi bao gồm, ví dụ, độ dài của mỗi bản nhạc? Bởi vì tôi tưởng tượng rằng khách hàng đang hiển thị một album có thể quan tâm bằng cách liệt kê các bản nhạc theo tiêu đề, nhưng cũng hiển thị độ dài của mỗi bản nhạc mà hầu hết các khách hàng làm. Mặt khác, tôi không thể hiển thị (các) nhà soạn nhạc hoặc nghệ sĩ cho mỗi bản nhạc, vì tôi quyết định rằng thông tin này không cần thiết ở cấp độ này. Rõ ràng, sự lựa chọn của bạn có thể khác nhau.

  2. GET /tracks/:trackIdtrả về thông tin về một bản nhạc cụ thể Vì không còn phân cấp nữa, bạn không cần phải đoán album hoặc nghệ sĩ: điều duy nhất bạn thực sự phải biết là định danh của bản nhạc.

    Hoặc thậm chí có thể không? Điều gì nếu bạn có thể chỉ định nó theo tên với 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
        }
    }

    Bây giờ nhìn gần hơn albums; bạn thấy gì? Phải, không phải một, mà là hai album. Nếu bạn có một hệ thống phân cấp, bạn không thể làm điều đó (trừ khi bạn sao chép bản ghi).

  3. GET /comments/:objectGid. Bạn có thể đã phát hiện ra các GUID xấu xí trong các câu trả lời. Những GUID đó cho phép xác định thực thể trên cơ sở dữ liệu để thực hiện các tác vụ có thể được áp dụng cho album hoặc nghệ sĩ hoặc bản nhạc. Chẳng hạn như bình luận.

    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"
        }
    ]

    Nhận xét tham chiếu đến đối tượng liên quan, làm cho nó có thể đi đến nó khi truy cập bình luận bên ngoài ngữ cảnh của nó (ví dụ khi kiểm duyệt các bình luận mới nhất thông qua GET /comments/latest).

Lưu ý rằng điều này không có nghĩa là bạn nên tránh mọi hình thức phân cấp trong API của mình. Có những trường hợp nó có ý nghĩa. Như một quy luật của:

  • Nếu tài nguyên không có ý nghĩa bên ngoài bối cảnh của tài nguyên mẹ, hãy sử dụng phân cấp.

  • Nếu tài nguyên có thể sống một mình (1) hoặc (2) trong bối cảnh tài nguyên gốc thuộc các loại khác nhau hoặc (3) có nhiều cha mẹ, thì không nên sử dụng hệ thống phân cấp.

Chẳng hạn, các dòng của một tệp không có ý nghĩa gì ngoài ngữ cảnh của một tệp, vì vậy:

GET /file/:fileId

và:

GET /file/:fileId/line/:lineIndex

tốt thôi.


Các bạn, từ tìm kiếm, tôi cũng có thể trả lại thông tin album đầy đủ, nó sẽ tạo ra một tài nguyên khác - SongSearchResult, nó ổn, tôi giả sử. Nhưng còn URL thì sao? Tôi có nên cung cấp cho parentIDtừng đối tượng và sử dụng nó làm tham số GET hoặc phần bình thường của url không? Nếu tôi có độ sâu> 2 thì sao? /artist/1/album/10/song/3/comment/23- thật điên rồ khi cung cấp mọi id của nghệ sĩ, album và bài hát trong commentđối tượng, nhưng tôi nghe nói đó là một cách để đi, nhưng không phải là khó hiểu?!
dt0xff

@ dt0xff: Tôi đã chỉnh sửa câu trả lời của mình. Tôi nghĩ rằng nó sẽ cung cấp cho bạn một hình ảnh rõ ràng về cách có thể tránh được độ sâu.
Arseni Mourzenko

Phải, rõ ràng bây giờ việc triển khai điểm nhập cho từng tài nguyên dễ dàng hơn nhiều (ngoại trừ một số dòng như hoặc điều chức năng khác) sẽ thêm nó vào cha mẹ bằng url. Cảm ơn bạn, bạn đã thuyết phục tôi rằng sự lựa chọn của tôi là đúng và "cách tiếp cận chung" (thực sự, nhiều thứ lồng nhau .. restangularđược xây dựng trên nó) không tốt cho lắm.
dt0xff 3/03/2015

Câu trả lời chính xác. Tôi có một vài phản đối mặc dù. "Nếu một album có nhiều nghệ sĩ thì sao?" Các URI khác nhau có thể xác định cùng một tài nguyên do URI quan hệ nhị phân -> tài nguyên là duy nhất đúng (nhiều-một). Vì vậy, các URI /artists/foo/albums/qux/artists/bar/albums/quxhoàn toàn có thể xác định cùng một tài nguyên album. Nói cách khác, thành phần đường dẫn trong URI đại diện cho hệ thống phân cấp biểu đồ , không nhất thiết là hệ thống phân cấp cây , điều này làm cho nó phù hợp để đại diện cho không chỉ các danh mục mà cả các thẻ.
Maggyero

1
Càng "Đây thực chất là vấn đề tôi đã giải thích trong blog của mình : một đại diện cho cây có quá nhiều hạn chế để được sử dụng hiệu quả trong nhiều trường hợp." Vì vậy, đây không phải là một vấn đề. "Điều gì sẽ xảy ra nếu bạn muốn thêm một tính năng giúp bình luận album?" Đó cũng không phải là vấn đề : /artists/foo/albums/qux/comments/7. "Nếu có ý kiến ​​bình luận thì sao?" Tương tự như vậy : /artists/foo/albums/qux/song/5/comments/2/comments/8.
Maggyero
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.