2011-02-07 23 views
10

Thực tiễn tốt nhất cho phiên bản tài nguyên REST là đưa thông tin phiên bản vào tiêu đề Chấp nhận/Loại nội dung của yêu cầu HTTP để nguyên URI.Phiên bản tài nguyên REST dễ dàng trong triển khai dựa trên JAX-RS?

Đây là yêu cầu mẫu/đối phó với REST API để lấy thông tin hệ thống: sự chú ý

==> 
GET /api/system-info HTTP/1.1 
Accept: application/vnd.COMPANY.systeminfo-v1+json 

<== 
HTTP/1.1 200 OK 
Content-Type: application/vnd.COMPANY.systeminfo-v1+json 
{ 
    “session-count”: 19 
} 

Pay rằng phiên bản được quy định trong định dạng MIME.

Dưới đây là một yêu cầu/đáp ứng cho phiên bản 2:

==> 
GET /api/system-info HTTP/1.1 
Accept: application/vnd.COMPANY.systeminfo-v2+json 

<== 
HTTP/1.1 200 OK 
Content-Type: application/vnd.COMPANY.systeminfo-v2+json 
{ 
    “uptime”: 234564300, 
    “session-count”: 19 
} 

Xem http://barelyenough.org/blog/tag/rest-versioning/ để biết thêm lời giải thích và ví dụ.

Có thể thực hiện phương pháp này dễ dàng trong các triển khai dựa trên JAX-RS được nhắm mục tiêu Java, chẳng hạn như Jersey hoặc Apache CXF không?

Mục tiêu là có một số lớp @Resource có cùng giá trị @Path nhưng phân phát yêu cầu dựa trên phiên bản thực tế được chỉ định trong loại MIME?

Tôi đã xem xét JAX-RS nói chung và Jersey trong phân khúc và không tìm thấy hỗ trợ cho điều đó. Jersey không có cơ hội đăng ký hai tài nguyên với cùng một đường dẫn. Thay thế cho lớp WebApplicationImpl cần phải được triển khai để hỗ trợ điều đó.

Bạn có thể đề xuất điều gì đó không?

LƯU Ý: Cần có nhiều phiên bản của cùng một tài nguyên cùng một lúc. Các phiên bản mới có thể đưa ra những thay đổi không tương thích.

+2

Điều này chắc chắn KHÔNG thực hành tốt nhất để phiên bản API. Cách tốt nhất là KHÔNG có phiên bản và chỉ thực hiện các thay đổi tương thích. Tạo nhân tạo các loại MIME mới cho những thay đổi mà mọi khách hàng hợp lý cần xử lý tự động (thêm thẻ/khóa mới vào dữ liệu của bạn) không phải là RESTful trong cuốn sách của tôi. –

+4

Vâng, KHÔNG phải lúc nào cũng có thể thực hiện các thay đổi tương thích. Hơn nữa ,, trong trường hợp của tôi, nhiều phiên bản tài nguyên REST cần được hỗ trợ đồng thời. Theo như bản sắc tài nguyên phải được bảo quản URI phải thay đổi giống nhau. Phiên bản mới là bản trình bày mới của tài nguyên, tức là loại MIME mới. –

+0

Cảm ơn bạn đã bình luận của bạn, tôi sẽ cập nhật câu hỏi ban đầu để cụ thể hơn –

Trả lời

6

JAX-RS gửi đến các phương thức được chú thích bằng @Produces thông qua tiêu đề Chấp nhận. Vì vậy, nếu bạn muốn JAX-RS thực hiện công việc của bạn, bạn sẽ cần tận dụng cơ chế này. Nếu không có thêm bất kỳ công việc nào, bạn sẽ phải tạo một phương thức (và Nhà cung cấp) cho mọi loại phương tiện mà bạn muốn hỗ trợ.

Không có gì ngăn bạn có một số phương pháp dựa trên loại phương tiện truyền thông mà tất cả gọi một phương pháp phổ biến để làm việc đó, nhưng bạn phải cập nhật và thêm mã mỗi lần bạn thêm loại phương tiện mới.

Một ý tưởng là thêm bộ lọc "chuẩn hóa" tiêu đề Chấp nhận của bạn một cách cụ thể để gửi đi. Đó là, có lẽ, lấy của bạn:

Accept: application/vnd.COMPANY.systeminfo-v1+json 

Và chuyển đổi đó để, chỉ cần:

Accept: application/vnd.COMPANY.systeminfo+json 

Đồng thời, bạn trích xuất các thông tin phiên bản để sử dụng sau (có lẽ trong yêu cầu, hoặc một số cơ chế đặc biệt khác).

Sau đó, JAX-RS sẽ gửi đến phương thức duy nhất xử lý "application/vnd.COMPANY.systeminfo + json".

Phương thức sau đó lấy thông tin phiên bản "ngoài băng" để xử lý chi tiết trong quá trình xử lý (chẳng hạn như chọn lớp thích hợp để tải qua OSGi).

Tiếp theo, sau đó bạn tạo Nhà cung cấp có MessageBodyWriter thích hợp. Nhà cung cấp sẽ được JAX-RS chọn cho kiểu ứng dụng/vnd.COMPANY.systeminfo + json. Nó sẽ được lên đến MBW của bạn để tìm ra các loại phương tiện truyền thông thực tế (dựa trên một lần nữa về thông tin phiên bản) và để tạo ra định dạng đầu ra thích hợp (một lần nữa, có lẽ gửi đến đúng lớp tải OSGi).

Tôi không biết liệu MBW có thể ghi đè tiêu đề Loại nội dung hay không. Nếu không, sau đó bạn có thể ủy nhiệm bộ lọc trước đó để viết lại phần đó cho bạn trên đường ra.

Đó là một chút phức tạp, nhưng nếu bạn muốn tận dụng công văn JAX-RS, và không tạo phương thức cho mọi phiên bản của loại phương tiện, thì đây là một cách có thể để thực hiện điều đó.

Chỉnh sửa để đáp ứng với bình luận:

Yea, về cơ bản, bạn muốn JAX-RS để gửi đến lớp đúng đắn dựa trên cả hai con đường và chấp nhận loại. Không chắc rằng JAX-RS sẽ thực hiện việc này ra khỏi hộp, vì nó là một chút của một trường hợp cạnh. Tôi đã không xem xét bất kỳ triển khai JAX-RS nào, nhưng bạn có thể thực hiện những gì bạn muốn bằng cách tinh chỉnh một trong các cấp độ cơ sở hạ tầng.

Có thể một tùy chọn ít xâm lấn khác là sử dụng mẹo cũ từ thế giới Apache và chỉ cần tạo bộ lọc viết lại đường dẫn của bạn dựa trên tiêu đề Chấp nhận.

Vì vậy, khi hệ thống được:

GET /resource 
Accept: application/vnd.COMPANY.systeminfo-v1+json 

Bạn viết lại nó để:

GET /resource-v1 
Accept: application/vnd.COMPANY.systeminfo-v1+json 

Sau đó, trong lớp JAX-RS của bạn:

@Path("resource-v1") 
@Produces("application/vnd.COMPANY.systeminfo-v1+json") 
public class ResourceV1 { 
    ... 
} 

Vì vậy, khách hàng của bạn có được cái nhìn chính xác, nhưng các lớp của bạn được JAX-RS gửi đi đúng cách. Vấn đề khác duy nhất là các lớp học của bạn, nếu họ nhìn, sẽ thấy Path đã sửa đổi, không phải đường dẫn ban đầu (nhưng bộ lọc của bạn có thể chứa nội dung đó trong yêu cầu dưới dạng tham chiếu nếu bạn muốn).

Nó không lý tưởng, nhưng hầu hết là miễn phí.

This là một bộ lọc hiện tại có thể làm những gì bạn muốn làm, nếu không nó có lẽ có thể hoạt động như một nguồn cảm hứng để bạn tự làm điều đó.

+0

Cảm ơn bạn rất nhiều vì câu trả lời. Điều này đang tiến gần hơn đến những gì tôi cần. Việc chuyển MIME sẽ thực hiện một phần công việc. Tuy nhiên tôi không muốn phương pháp tài nguyên để đối phó với thông tin phiên bản. Lớp Tài nguyên toàn bộ phải đại diện cho phiên bản Tài nguyên cụ thể VÀ sẽ có một số lớp Tài nguyên trong thời gian chạy, ví dụ: RestService trong gói 1.0 và RestService trong gói 2.0, cả hai đều có @Path ('/ rest'). Tôi muốn hướng dẫn Jersey để differenciate giữa hai mà không cần viết lại WebApplicationImpl nếu có thể. (nếu không thể thì tôi sẽ tiếp tục và viết lại/mở rộng nó) –

+0

Đây có lẽ là câu trả lời hay nhất cho đến bây giờ. Lý tưởng nhất, tôi sẽ không có phiên bản trong @Path, @Sản phẩm và tên lớp tài nguyên cho lớp của tôi vì phiên bản phải được lấy từ bộ định dạng phiên bản gói OSGi. Nhưng một lần nữa, đó là kịch bản IDEAL. Bạn đã cho bạn gợi ý rất hữu ích ở đây. Cảm ơn bạn! –

0

Một giải pháp khả thi là sử dụng một @Path với

Content-Type: application/vnd.COMPANY.systeminfo- {version} + json

Sau đó, bên trong phương pháp của @Path cho trước, bạn có thể gọi phiên bản của WebService

+0

Vấn đề là tôi không muốn di chuyển logic phiên bản cụ thể ngay bên trong việc triển khai phương thức. Tôi muốn điều đó được xử lý bên ngoài. –

+0

Dù sao, cảm ơn bạn đã phản hồi :) –

+0

Dù bằng cách nào bạn cũng sẽ phải "tạo một lớp để chọn phương thức để gọi". Cách bạn đang đề xuất lớp này sẽ gọi một phương thức cụ thể dựa trên nội dung @Consumes. Giải pháp tôi đã đăng bạn sẽ chỉ có một @Consumes và chọn phương thức để gọi bên trong. Đối với tôi, nó chỉ giống nhau, một cách bạn xử lý bằng chú thích và nhân rộng trên các phương thức. Cách khác bạn sao chép các cuộc gọi phương thức. –

0

Nếu bạn đang sử dụng CXF, bạn có thể use the technique specified here để xây dựng nhà cung cấp serialization mới (xây dựng cơ sở hạ tầng hiện có) tạo dữ liệu theo định dạng cụ thể mong muốn. Khai báo một vài cái, một cho mỗi định dạng cụ thể mà bạn muốn và sử dụng chú thích @Produces để cho phép máy móc xử lý phần còn lại của thương lượng cho bạn, mặc dù cũng có thể là ý tưởng hỗ trợ loại nội dung JSON chuẩn để khách hàng bình thường có thể xử lý nó mà không cần phải bẻ khóa sự đặc biệt của bạn.Câu hỏi thực sự duy nhất sau đó trở thành cách tốt nhất để thực hiện tuần tự hóa là gì; Tôi đoán bạn có thể con số đó ra cho chính mình ...


[EDIT]: đào sâu hơn trong CXF documentation dẫn đến sự mặc khải rằng cả @Consumes@Produces chú thích được coi là trục để thực hiện lựa chọn. Nếu bạn muốn có hai phương pháp xử lý việc sản xuất phản hồi cho các loại phương tiện khác nhau, bạn chắc chắn nhất có thể. (Bạn sẽ phải thêm các nhà cung cấp tuần tự hóa và/hoặc deserialization nếu bạn đang sử dụng các loại tùy chỉnh, nhưng bạn có thể làm đại biểu của phần lớn công việc cho các nhà cung cấp tiêu chuẩn.) Tôi vẫn muốn thận trọng rằng bạn nên vẫn đảm bảo rằng tài nguyên được chỉ ra bởi đường dẫn phải giống nhau trong cả hai trường hợp; để làm khác không phải là RESTful.

+0

Donal, cảm ơn bạn đã phản hồi. Bạn đã chỉ ra gợi ý cho nhà cung cấp tuần tự hóa, là một phần của việc chuẩn bị _output_. Câu hỏi quan trọng hơn là làm thế nào để làm cho quá trình _input_ và làm thế nào để đăng ký một số lớp tài nguyên theo cùng một đường dẫn. –

+0

@Volodymyr: Đăng ký một số lớp tài nguyên dưới cùng một đường dẫn? Điều đó chắc chắn * không thể * được RESTful! Toàn bộ vấn đề là bạn đang phơi bày nhiều lượt xem của cùng một tài nguyên cơ bản. Các bản trình bày JSON khác nhau phải đơn giản là cách khác nhau để xem một thứ. (Serializers phải được dạy làm thế nào để xây dựng các quan điểm khác nhau, nhưng đó là những gì bạn nhận được để đi đến loại phức tạp.) –

+0

Và cho đầu vào (tức là, '@ Consumes' chú thích) bạn chỉ cần phải đối phó với những gì bạn đã được đưa ra. Khách hàng có xu hướng ghét nó nếu bạn vứt bỏ những gì họ đã gửi lại trên khuôn mặt của họ (khi tôi có mã đã làm điều đó, nó gây ra những mối liên hệ lớn cho đồng nghiệp của tôi, người đã viết thư viện khách hàng đồng hành ...) –

Các vấn đề liên quan