API REST - API có nên trả về các đối tượng JSON lồng nhau không?


37

Khi nói đến API JSON, cách tốt nhất là làm phẳng các phản hồi và tránh các đối tượng JSON lồng nhau?

Như một ví dụ cho phép chúng ta có một API tương tự IMDb nhưng dành cho các trò chơi video. Có một vài thực thể, Trò chơi, Nền tảng, ESRBRating và GamePl PlatformMap để ánh xạ Trò chơi và Nền tảng.

Hãy nói rằng bạn yêu cầu / trò chơi / 1 tìm nạp trò chơi bằng ID 1 và nó trả về đối tượng trò chơi với các nền tảng và esrbRating lồng nhau.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"id":1,"name":"Xbox"},
    {"id":2,"name":"Playstation"}
  ],
  "esrbRating": {
    "id": 1,
    "code": "E",
    "name": "Everyone"
  }
}

Nếu bạn đang sử dụng một cái gì đó như JPA / Hibernate, nó có thể tự động làm điều này cho bạn nếu nó được đặt thành FETCH.EAGER.

Tùy chọn khác là chỉ đơn giản là API và thêm nhiều điểm kết thúc.

Trong trường hợp đó khi / game / 1 được yêu cầu, đối tượng trò chơi được trả về.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
}

Nếu bạn muốn các nền tảng và / hoặc ESRBRating, bạn sẽ phải gọi như sau:

/ trò chơi / 1 / nền tảng / trò chơi / 1 / esrb

Phương pháp này có vẻ như có khả năng có thể thêm nhiều cuộc gọi đến máy chủ tùy thuộc vào dữ liệu nào khách hàng cần và khi nào họ cần.

Có một suy nghĩ cuối cùng tôi đã có nơi bạn sẽ có một cái gì đó như thế này trở lại.

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": ["Xbox","Playstation"]
}

Tuy nhiên, điều này giả định rằng họ không cần ID hoặc bất kỳ thông tin nào khác có thể được liên kết với các đối tượng nền tảng đó.

Nói chung, tôi hỏi cách tốt nhất để cấu trúc các đối tượng JSON được trả về từ API của bạn là gì. Bạn có nên cố gắng ở gần các thực thể của mình nhất có thể không, hoặc sử dụng Đối tượng miền hoặc Đối tượng truyền dữ liệu có ổn không? Tôi hiểu các phương thức sẽ có sự đánh đổi, hoặc nhiều công việc hơn trên lớp truy cập dữ liệu hoặc nhiều công việc hơn cho khách hàng.

Tôi cũng muốn nghe câu trả lời liên quan đến việc sử dụng Spring MVC làm công nghệ phụ trợ cho API, với JPA / Hibernate hoặc MyBatis để duy trì.


6
Những phản đối nào, nếu có, bạn có trả lại các đối tượng nhúng không? Việc trả lại các đối tượng nhúng riêng lẻ từ các điểm cuối khác nhau sẽ gây khó chịu khá nhiều (chưa kể chậm).
Robert Harvey

1
Cá nhân tôi không phản đối nó. Tôi chỉ không nhận thức được những gì được coi là thực hành tốt nhất. Một đồng nghiệp tuyên bố làm việc với các đối tượng nhúng trong AngularJS không đơn giản và cuối cùng tôi muốn ứng dụng Ember of AngularJS sử dụng API. Tôi không biết đủ về Angular hay Ember để biết liệu điều đó có ảnh hưởng hay không.
greyfox

3
Câu trả lời sẽ phụ thuộc vào việc bạn muốn trả về các đối tượng miền, các đối tượng DTO, ViewModel hoặc KitchenSink. Đối tượng nào bạn quay lại rất có thể sẽ được xác định bởi ứng dụng của bạn cần gì và đối tượng nói như thế nào hành xử qua Internet. Ví dụ: nếu bạn đang cố gắng điền vào một trang web dữ liệu từ hóa đơn, rất có thể bạn sẽ trả lại một đối tượng chứa mọi thứ bạn cần (trừ khi bạn có kế hoạch AJAXing trong các chi tiết đơn hàng hoặc đại loại như thế).
Robert Harvey

Đó là trường hợp này khi bạn yêu cầu một trò chơi, bạn có thể muốn biết các thể loại, nền tảng và ESRBRating. Điều đó có ý nghĩa. Về mặt thiết kế theo quan điểm Java, bạn có khuyên bạn nên có gói Entity có JPA mời, và sau đó là gói miền là đối tượng kinh doanh / DTO được trả lại cho người dùng không?
greyfox

1
Các cuộc gọi đến máy chủ là đắt tiền. API yêu cầu bạn gửi dữ liệu bằng nhiều cuộc gọi sẽ chậm hơn API cho phép bạn nhận mọi thứ trong một cuộc gọi, thường là ngay cả khi cuộc gọi sau trả về thông tin không cần thiết.
Gort Robot

Câu trả lời:


11

Một cách khác (sử dụng HATEOAS). Điều này rất đơn giản, chủ yếu là trong thực tế, bạn thêm một thẻ liên kết trong json tùy thuộc vào việc bạn sử dụng HATEOAS.

http://api.example.com/games/1:

{
  "id": 1,
  "title": "Game A",
  "publisher": "Publisher ABC",
  "developer": "Developer DEF",
  "releaseDate": "2015-01-01",
  "platforms": [
    {"_self": "http://api.example.com/games/1/platforms/53", "name": "Playstation"},
    {"_self": "http://api.example.com/games/1/platforms/34", "name": "Xbox"},
  ]
}

http://api.example.com/games/1/platforms/34:

{
  "id": 34,
  "title": "Xbox",
  "publisher": "Microsoft",
  "releaseDate": "2015-01-01",
  "testReport": "http://api.example.com/games/1/platforms/34/reports/84848.pdf",
  "forms": [
    {"type": "edit", "fields: [] },
  ]
}

Tất nhiên bạn có thể nhúng tất cả dữ liệu vào tất cả các danh sách nhưng điều đó có thể sẽ là quá nhiều dữ liệu. Bằng cách này, bạn có thể nhúng dữ liệu cần thiết và sau đó tải thêm nếu bạn thực sự muốn làm việc với nó.

Việc thực hiện kỹ thuật có thể chứa bộ nhớ đệm. Bạn có thể lưu trữ các liên kết và tên nền tảng trong đối tượng trò chơi và gửi nó ngay lập tức mà không cần phải tải api nền tảng. Sau đó khi cần bạn có thể tải nó.

Bạn thấy ví dụ tôi đã thêm một số thông tin mẫu. Tôi đã làm điều đó để cho bạn thấy có thể có nhiều thông tin hơn trong một đối tượng json chi tiết hơn là bạn thậm chí muốn tải trong danh sách các trò chơi.


Tôi không nghĩ đó là HATEOS về mặt kỹ thuật vì không có nhà nước.
RibaldEddie

Vâng, không chắc chắn từ chính xác về quá trình này. HATEOS nói chung đang được sử dụng để liên kết trong các API còn lại nhưng tôi đồng ý rằng nó cũng phải liên quan đến trạng thái. Mặc dù ý tưởng thực hiện sẽ giống nhau. Ở đây bạn thấy thêm một chút về cách sử dụng nó bằng một ví dụ: Stormpath.com/blog/linking-and-resource-Exansion-rest-api-tips
Luc Franken

Đó là một ý tưởng tốt mặc dù!
RibaldEddie

1
Nếu bạn đang phát triển API trong đó có sự gắn kết giữa máy khách và chính api (giả sử là api nội bộ), việc trả lại phản hồi lồng nhau (hoặc làm phẳng) sẽ có ý nghĩa hơn là cung cấp liên kết đến tài nguyên khác, có nghĩa là nhiều yêu cầu API hơn mà có thể là không mong muốn.
Bruno

@bruno có nhưng với một giới hạn: Trên các hệ thống lớn hơn, bạn không thể hoặc không muốn cung cấp đầy đủ tất cả các đối tượng liên quan. Các trường bạn bao gồm theo mặc định là tùy ý, bạn có thể chọn chúng dựa trên việc sử dụng api của bạn. Vì vậy, trong trường hợp này, bạn có thể có các nền tảng với hàng trăm trường, trường hợp sử dụng đang hiển thị một hộp chọn để chọn một nền tảng. Sau đó, thật hợp lý khi bao gồm tên của nền tảng nhưng nó không cần các chi tiết tài chính của nền tảng chẳng hạn.
Luc Franken

16

Đây là một trong những câu hỏi cơ bản khi nói đến thiết kế API REST. Mỗi nhà thiết kế tự hỏi mình câu hỏi này vào ngày đầu tiên. Xin lỗi nhưng câu trả lời là "nó phụ thuộc". Mỗi cách tiếp cận đều có ưu và nhược điểm và bạn sẽ chỉ cần đưa ra quyết định và đi theo nó.


10
Điều này hoàn toàn không hữu ích. Bản thân OP cũng biết "nó phụ thuộc và mỗi cách tiếp cận đều có ưu và nhược điểm". Bạn nên giải thích về những gì nó phụ thuộc hoặc ít nhất là đưa ra một số ví dụ.
Pratik Singhal

5

Tôi thứ hai cách tiếp cận được trình bày ở đây https://www.sl slideshoware.net/stormpath/rest-jsonapis

Nói tóm lại, bao gồm tài nguyên lồng nhau dưới dạng liên kết trong tài nguyên gốc, trong khi đó, cung cấp một tham số mở rộng trong điểm cuối cha.

Theo tôi, đây là một cách hiệu quả và linh hoạt trong hầu hết các trường hợp.


2
Tôi thích cách tiếp cận này. Đối với bất cứ ai tự hỏi, điều này bắt đầu ở SLIDE 57 trong trình chiếu được liên kết.
Adam Plocher
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.