2011-11-17 2 views
5

Chúng tôi có một dịch vụ web và chạy được xây dựng trong VS2010.Có cách nào tốt để mở rộng dịch vụ WCF bằng cách sử dụng basicHttpBinding để cũng cho phép dịch vụ REST giao tiếp với JSON không?

Một số hợp đồng hoạt động trông như thế này:

[OperationContract] 
    ITicket Login(string userName, byte[] passwordHash, string softwareVersion); 

Tức là họ gian hàng có các đối số phức tạp và các kiểu trả về phức tạp, hoặc thậm chí nhiều trả về.

Gần đây, chúng tôi đã bắt đầu một dự án iPhone được thuê ngoài và cho phép họ sử dụng dịch vụ này để liên lạc với máy chủ của chúng tôi. Từ những gì tôi đã học được từ họ, tôi hiểu rằng đây không phải là một thực hành tốt để giao tiếp với iPhone (ví dụ như thiếu các cách tốt để tiêu thụ WSDL). Và do đó tôi đã bắt đầu xem xét khả năng phơi bày dịch vụ như một dịch vụ REST giao tiếp với JSON.

Tôi đã thêm một thiết bị đầu cuối mới, sử dụng webHttpBinding, trang trí hợp đồng như thế này:

[OperationContract] 
    [WebGet(UriTemplate = "/login?username={userName}&password={password}&softwareVersion={softwareVersion}", ResponseFormat=WebMessageFormat.Json)] 
    ITicket Login(string userName, string password, string softwareVersion); 

Phương pháp này hiện đang làm việc như dự định.

sau đó tôi đã cố gắng để trang trí một phương pháp khác như thế này:

[OperationContract] 
    [WebGet(UriTemplate = "/GetMetaData?ticket={ticket}",RequestFormat=WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] 
    IMetaData GetMetaData(ITicket ticket); 

Khi tôi bây giờ cố gắng truy cập này, tôi nhận được lỗi sau:

Server Error in '/Jetas5MobileService' Application. Operation 'GetMetaData' in contract 'IJetas5MobileService2' has a query variable named 'ticket' of type 'Jetas.MobileService.DataContracts.ITicket', but type 'Jetas.MobileService.DataContracts.ITicket' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter'.

Tôi có quản lý để xây dựng một OperationContract rằng chỉ lấy một chuỗi làm đối số và sau đó phân tích cú pháp mỏng ở mặt sau bằng cách sử dụng DataContractJsonSerializer, nhưng điều đó cảm thấy giống như một hack xấu xí.

Có cách nào để giải quyết vấn đề này theo cách tốt hơn không? Tôi là người mới bắt đầu khi nói đến WCF và REST vì vậy đừng ngần ngại chỉ cho tôi bất kỳ hướng dẫn mới bắt đầu nào có thể có ở đó. Tôi đã cố gắng tìm kiếm chúng nhưng số lượng nguồn khổng lồ làm cho việc tìm kiếm những nguồn tốt trở nên khó khăn.

+0

bạn đang sử dụng phiên bản WCF nào? –

+0

Tôi đang sử dụng .net4 và VS2010, điều đó có trả lời được câu hỏi không? Khác cho tôi biết làm thế nào tôi có thể tìm nó lên. –

Trả lời

2

From what I have learnt from them I understood that this is not a good practice for communicating to the iPhone (lack of good ways to consume the WSDL for example).

Vấn đề lớn nhất không phải là thiếu "công cụ" tốt nhưng thiếu hiểu biết WSDL là gì và cách hoạt động của dịch vụ web. Tất cả những công cụ này tạo ra các cuống dịch vụ cho các nhà phát triển đã khiến các nhà phát triển không hiểu những gì đang ở dưới mui xe. Nó hoạt động cho các kịch bản cơ bản, nơi mọi phép thuật được thực hiện cho bạn nhưng một khi nhà phát triển phải theo dõi bất kỳ vấn đề nào hoặc mở rộng "công cụ" với các tính năng bổ sung, chúng có vấn đề lớn (và thường dẫn đến giải pháp xấu). Để được trung thực phát triển SW không phải là về kịch bản cơ bản.

REST đặt một thách thức lớn cho các nhà phát triển bởi vì nó không cung cấp bất kỳ công cụ "ma thuật" nào. REST là về việc sử dụng đúng giao thức HTTP và nó tận dụng tối đa cơ sở hạ tầng HTTP hiện có. Nếu không có kiến ​​thức cơ bản về giao thức HTTP, bạn sẽ không tạo ra dịch vụ REST tốt. Đó là nơi bạn nên bắt đầu.

Dưới đây là một số ví dụ về việc sử dụng không đúng:

[OperationContract] 
[WebGet(UriTemplate = "/login?username={userName}&password={password}&softwareVersion={softwareVersion}", ResponseFormat=WebMessageFormat.Json)] 
ITicket Login(string userName, string password, string softwareVersion); 

Login phương pháp rõ ràng là một cái gì đó mà thực hiện một số hành động - Tôi đoán nó tạo ra vé. Nó hoàn toàn không phù hợp với yêu cầu GET HTTP. Điều này chắc chắn sẽ là một yêu cầu POST đối với tài nguyên Đăng nhập, trả về biểu trưng mới ITicket cho mỗi cuộc gọi. Tại sao? Bởi vì các yêu cầu GET được cho là an toàn và không đáng tin cậy.

  • An toàn: yêu cầu không được gây ra bất kỳ tác dụng phụ nào = không nên thay đổi tài nguyên nhưng trong trường hợp của bạn, có thể tạo tài nguyên mới.
  • Idempotent: điều này không quan trọng đối với ví dụ vì bạn đã vi phạm quy tắc An toàn nhưng điều đó có nghĩa là yêu cầu tài nguyên phải lặp lại được. Nó có nghĩa là yêu cầu đầu tiên với cùng một tên người dùng, mật khẩu và phiên bản có thể tạo tài nguyên mới nhưng khi yêu cầu được thực hiện lại, nó không nên tạo tài nguyên mới nhưng trả lại đã tạo. Điều này có ý nghĩa hơn khi tài nguyên được duy trì/duy trì trên máy chủ.

Bởi vì yêu cầu HTTP GET là bởi cơ sở hạ tầng HTTP được coi là an toàn và không đáng tin cậy được xử lý theo cách khác. Ví dụ yêu cầu GET có thể được lưu trữ chuyển hướng vv Khi yêu cầu không an toàn và idempotent nó nên sử dụng phương thức POST. Vì vậy, định nghĩa chính xác là:

[OperationContract] 
[WebInvoke(UriTemplate = "/login?username={userName}&password={password}&softwareVersion={softwareVersion}", ResponseFormat=WebMessageFormat.Json)] 
ITicket Login(string userName, string password, string softwareVersion); 

WebInvoke mặc định là phương thức POST. Đó cũng là lý do tại sao tất cả các đường hầm giao thức (ví dụ SOAP) thường sử dụng các phương thức POST HTTP cho tất cả các yêu cầu.

Sự cố khác trong ví dụ trước có thể là phương pháp REST = tận dụng toàn bộ cơ sở hạ tầng HTTP. Nó nên sử dụng xác thực dựa trên HTTP (đăng nhập) = Cơ bản, Thông báo, OAuth, v.v. Nó không có nghĩa là bạn không thể có tài nguyên tương tự nhưng trước tiên bạn nên xem xét sử dụng cách HTTP chuẩn.

Ví dụ thứ hai của bạn thực sự tốt hơn nhiều nhưng có vấn đề với giới hạn WCF. WCF có thể đọc từ URL chỉ các loại cơ bản (btw. Làm thế nào để bạn muốn vượt qua đối tượng trong URL?). Bất kỳ loại tham số nào khác cần hành vi WCF tùy chỉnh. Nếu bạn cần để lộ phương pháp mà chấp nhận hợp đồng dữ liệu bạn một lần nữa phải sử dụng phương thức HTTP mà chấp nhận các thông số trong cơ thể - một lần nữa sử dụng POST và nơi JSON vé serialized cho cơ thể được yêu cầu:

[OperationContract] 
[WebInvoke(UriTemplate = "/GetMetaData",RequestFormat=WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json)] 
IMetaData GetMetaData(ITicket ticket); 
+0

Cảm ơn bạn đã phản hồi rất tốt! Tôi sẽ đọc thêm về giao thức HTTP. Tôi đã có một câu hỏi rồi. GetMetaData về cơ bản là một truy vấn trả về dữ liệu, tùy thuộc vào vé. Khi tôi đọc http://www.w3.org/2001/tag/doc/whenToUseGet.html#checklist tôi giải thích điều này vì GET sẽ phù hợp nhất với hành động này, tức là trạng thái của máy chủ là như nhau. Có phải vì loại đối số phức tạp mà POST được ưa thích không? –

+0

Có nó phù hợp hơn cho GET nhưng vì tính năng mặc định WCF đặt định nghĩa phương thức của bạn yêu cầu POST để chấp nhận loại phức tạp. –

2

Tôi gặp vấn đề tương tự khi sử dụng WCF Rest Starter Kit.

Nếu tôi nhớ chính xác, các biến UriTemplate trong đường dẫn luôn phân giải thành chuỗi khi sử dụng WebGet hoặc WebInvoke. Bạn chỉ có thể liên kết các biến UriTemplate thành int, long, vv khi chúng nằm trong phần truy vấn của UriTemplate. Vì vậy, không có phương tiện để vượt qua một đối tượng phức tạp trong.

Tôi nghĩ rằng không có cách nào để làm điều đó. Tôi chỉ sử dụng giải pháp phân tích cú pháp như bạn.

Bây giờ, bạn có thể kiểm tra ngăn xếp mới để thực hiện REST với WCF được gọi là WCF Web Api. Nó đề cập rất độc đáo với các kiểu phức tạp như các tham số của phương thức.

+0

Cảm ơn! Tôi sẽ xem xét cái đó. –

1

Bạn nên gửi dữ liệu JSON phương pháp và có thể thiết lập các khai báo như sau:

[OperationContract] 
    [WebInvoke(Method = "POST", RequestFormat = WebMessageFormat.Json, ResponseFormat = WebMessageFormat.Json, 
     UriTemplate = "/login", BodyStyle = WebMessageBodyStyle.Wrapped)] 
    ITicket Login(string userName, string password, string softwareVersion); 

sau đó, thêm một thiết bị đầu cuối mới với cấu hình của bạn như sau (để lại các điểm cuối và cấu hình hiện tại của bạn theo cách nó đứng, bạn chỉ việc thêm các thiết bị đầu cuối JSON mới và một mới hành vi):

<service behaviorConfiguration="Your.ServiceBehavior.Here" name="Your.Stuff.Here"> 
     <endpoint address="" binding="basicHttpBinding" bindingConfiguration="basicBindingSettings" behaviorConfiguration="basic" contract="Your.Contract.Here"> 
     </endpoint> 
     <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> 
     <endpoint address="json" binding="webHttpBinding" contract="Your.Contract.Here" behaviorConfiguration="web"></endpoint> 
     </service> 
<behaviors> 
     <endpointBehaviors> 
     <behavior name="web"> 
      <webHttp /> 
     </behavior> 
    </behaviors> 

Sau đó, bạn có thể đăng nội dung như "{" userName ":" testuser "," password ":" testpass "," softwareVersion ":" 1.0.0 "}" vào URL https://yourdomain.com/service.svc/json/login.

Nếu bạn muốn chuyển sang loại phức tạp thì bạn chỉ cần chuyển vào JSON khớp với đối tượng tùy chỉnh. Vì vậy, nếu bạn có một đối tượng động vật với các thuộc tính màu sắc và kích thước, JSON sẽ trông giống như "{" animal ": {" Color ":" red "," Size ":" Large "}}".

Điều đó nên thực hiện và bạn không cần thay đổi cách triển khai phương pháp. WCF sẽ chỉ trả về dữ liệu định dạng JSON khi được gọi theo cách trên, so với điểm cuối JSON. Các phương thức SOAP hiện có của bạn sẽ tiếp tục hoạt động như bình thường.

+0

Cả điểm cuối và phương thức "đăng nhập" của tôi đều phù hợp với tôi. Đó là khi tôi thử với các loại phức tạp hơn mà lỗi bắt đầu. –

+0

Tôi nhận thấy bạn đã có một số thứ trong bài đăng của tôi đã hoàn thành, nhưng tôi đã thực hiện các bước từ trên xuống dưới để hoàn thành, chỉ trong trường hợp người khác tình cờ gặp phải điều này. Chìa khóa với các kiểu phức tạp trong kịch bản mà tôi đã mô tả là thiết lập RequestFormat = WebMessageFormat.Json và sau đó định dạng đúng yêu cầu JSON của bạn. – GCaiazzo