21

Tôi gặp phải vấn đề tại nơi tôi không thể tìm thấy thông tin về tiêu chuẩn hoặc thực hành thông thường để thực hiện các hoạt động CRUD trong dịch vụ web RESTful đối với tài nguyên có khóa chính là tổng hợp id tài nguyên. Chúng tôi đang sử dụng MVC WebApi để tạo bộ điều khiển. Ví dụ, chúng tôi có ba bảng:Dịch vụ REST tài nguyên khóa tổng hợp

  • Product: PK = ProductID
  • Part: PK = PartId
  • ProductPartAssoc: PK = (ProductID, PartId)

Một sản phẩm có thể có nhiều các bộ phận và một bộ phận có thể là một thành phần của nhiều sản phẩm. Bảng liên kết cũng chứa thông tin bổ sung có liên quan đến bản thân liên kết hơn là cần phải chỉnh sửa.

Chúng tôi có ProductsControllerPartsController lớp có thể xử lý thông thường GET/PUT/POST/DELETE hoạt động sử dụng các mẫu đường định nghĩa là: {controller}/{id}/{action} như vậy mà IRI sau làm việc:

  • GET, POST /api/Products - trả về tất cả các sản phẩm , tạo ra một sản phẩm mới
  • GET, PUT, DELETE /api/Products/1 - lấy/cập nhật/xóa sản phẩm 1
  • GET, POST /api/Parts - trả về tất cả các bộ phận, tạo ra một phần mới
  • GET, PUT, DELETE /api/Parts/2 - lấy/cập nhật/xóa phần 2
  • GET /api/Products/1/Parts - nhận được tất cả phụ tùng cho sản phẩm 1
  • GET /api/Parts/2/Products - nhận được tất cả các sản phẩm mà phần 2 là một thành phần

đâu Tôi đang gặp rắc rối là làm thế nào để xác định mẫu tuyến cho tài nguyên ProductPartAssoc. Mẫu tuyến đường và IRI trông như thế nào để nhận dữ liệu liên kết? Tôn trọng những quy ước, tôi mong chờ một cái gì đó như:

  • GET, POST /api/ProductPartAssoc - trả về tất cả các hiệp hội, tạo ra một hiệp hội
  • GET, PUT, DELETE /api/ProductPartAssoc/[1,2] - lấy/cập nhật/xóa mối liên hệ giữa sản phẩm 1 và một phần 2

đồng nghiệp của tôi tìm thấy điều này về mặt thẩm mỹ làm bực mình dù và dường như nghĩ rằng nó sẽ tốt hơn nếu không có một lớp ProductPartAssocController ở tất cả, nhưng đúng hơn, thêm các phương pháp bổ sung cho ProductsController để quản lý các dữ liệu liên quan:

  • GET, PUT, DELETE /api/Products/1/Parts/2 - nhận dữ liệu cho mối liên hệ giữa sản phẩm 1 và phần 2 chứ không phải là dữ liệu cho phần 2 là một thành viên của phần 1, mà thông thường sẽ là trường hợp dựa trên ví dụ khác như /Book/5/Chapter/3 mà tôi đã thấy ở nơi khác.
  • POST Không có đầu mối nào ở đây những gì họ mong đợi IRI trông như thế nào. Thật không may, họ là những người ra quyết định.

Vào cuối ngày, tôi đoán những gì tôi đang tìm kiếm là xác thực hoặc chỉ đường mà tôi có thể trỏ đến và nói "Xem, đây là những gì người khác làm".

Thực tiễn điển hình để xử lý các tài nguyên được xác định bằng khóa tổng hợp là gì?

+1

Đối với các đối tượng liên kết, bạn có thể mô hình không gian uri của bạn như giao thức OData chỉ định cho các dịch vụ Odata. tôi không gợi ý để thực hiện một dịch vụ OData, nhưng giá trị của nó nhìn vào nó vì nó cung cấp một cái nhìn sâu sắc hữu ích và gần gũi hơn với vấn đề của bạn. Nhìn vào đây để quản lý các liên kết giữa các thực thể: http: //www.odata.org/documentation/odata-v2-documentation/operation/# 29_Manipulating_Links –

+0

ProductPartAssoc trông như thế nào? Và, các hoạt động CRUD mà nó hỗ trợ là gì? –

+0

Ví dụ này là tùy ý, nhưng lớp ProductPartAssoc có thể trông giống như (xin lỗi vì terseness - giới hạn không gian ở đây): class ProductPartAssoc {int ProductId; int PartId; int amountUsed; decimal assemblyCost; int installerId; } và sẽ cần phải hỗ trợ tất cả bốn CRUD ops - Tạo, Lấy, Cập nhật, Xóa. – Mitselplik

Trả lời

17

Tôi cũng thích tính thẩm mỹ của /api/Products/1/Parts/2. Bạn cũng có thể có nhiều tuyến đường đi đến cùng một hành động, do đó bạn có thể tăng gấp đôi và cũng có thể cung cấp /api/Parts/2/Products/1 làm URL thay thế cho cùng một tài nguyên.

Đối với POST, bạn đã biết khóa tổng hợp. Vậy tại sao không loại bỏ nhu cầu POST và chỉ sử dụng PUT cho cả việc tạo và cập nhật? POST tới URL tài nguyên bộ sưu tập rất tuyệt nếu hệ thống của bạn tạo khóa chính, nhưng trong trường hợp bạn có tổng hợp các khóa chính đã biết, tại sao bạn cần POST?

Điều đó nói rằng, tôi cũng thích ý tưởng có một riêng biệt ProductPartAssocController để chứa các hành động cho các URL này. Bạn sẽ phải làm một ánh xạ tuyến đường tùy chỉnh, nhưng nếu bạn đang sử dụng một cái gì đó như AttributeRouting.NET đó là rất dễ dàng để làm.

Ví dụ chúng ta làm điều này để quản lý người dùng trong vai trò:

PUT, GET, DELETE /api/users/1/roles/2 
PUT, GET, DELETE /api/roles/2/users/1 

6 URL, nhưng chỉ có 3 hành động, tất cả trong GrantsController (chúng ta gọi là gerund giữa người sử dụng và vai trò của một "Grant"). Lớp học kết thúc bằng một cái gì đó như thế này, sử dụng AttributeRouting.NET:

[RoutePrefix("api")] 
[Authorize(Roles = RoleName.RoleGrantors)] 
public class GrantsController : ApiController 
{ 
    [PUT("users/{userId}/roles/{roleId}", ActionPrecedence = 1)] 
    [PUT("roles/{roleId}/users/{userId}", ActionPrecedence = 2)] 
    public HttpResponseMessage PutInRole(int userId, int roleId) 
    { 
     ... 
    } 

    [DELETE("users/{userId}/roles/{roleId}", ActionPrecedence = 1)] 
    [DELETE("roles/{roleId}/users/{userId}", ActionPrecedence = 2)] 
    public HttpResponseMessage DeleteFromRole(int userId, int roleId) 
    { 
     ... 
    } 

    ...etc 
} 

Điều này có vẻ là một cách tiếp cận khá trực quan với tôi. Giữ các hành động trong một bộ điều khiển riêng biệt cũng làm cho các bộ điều khiển gọn gàng hơn.

+1

Cảm ơn sự thấu hiểu :-) Hai điều: (1) Cảm ơn bạn đã tìm kiếm mẹo trên AttributeRouting.Net - những thứ tuyệt vời. (2) Nhìn lại, bạn đúng về việc không cần một hoạt động POST. Cảm ơn bạn đã chỉ ra điều đó. Tôi đã đánh dấu câu trả lời của bạn là trả lời câu hỏi của tôi ... – Mitselplik

0

Tôi đề nghị:

  • POST /api/PartsProductsAssoc: Tạo mối liên hệ giữa một phần và sản phẩm. Bao gồm id phần và sản phẩm trong dữ liệu POST.
  • NHẬN, PUT, XÓA /api/PartsProductsAssoc/<assoc_id>: đọc/cập nhật/xóa liên kết với <assoc_id> (không phải là một phần hoặc id sản phẩm, vâng, điều này có nghĩa là tạo cột mới trong bảng PartsProductsAssoc).
  • GET /api/PartsProductsAssoc/Parts/<part_id>/Products: nhận danh sách các sản phẩm được liên kết với phần đã cho.
  • NHẬN /api/PartsProductsAssoc/Products/<product_id>/Parts: nhận danh sách các bộ phận được liên kết với sản phẩm nhất định.

lý do để áp dụng cách này:

  • Độc thân, đầy đủ điều kiện URI cho mỗi liên kết.
  • Sửa đổi liên kết sửa đổi một tài nguyên REST đơn lẻ.

Để biết thêm thông tin, hãy xem https://www.youtube.com/watch?v=hdSrT4yjS1g lúc 56:30.