2011-06-29 43 views
6

(Cập nhật lúc kết thúc)

Tôi đang nghiên cứu ý tưởng sử dụng công nghệ không quen thuộc. Tôi đã viết một vài dịch vụ WCF, nhưng tôi chưa bao giờ thực hiện bất kỳ cấu hình nâng cao nào. Đây là lần đầu tiên tôi đi sâu vào jQuery. Tiền đề là tôi tạo ra một dịch vụ WCF để lấy thông tin chi nhánh, được truy xuất bởi jQuery.

Tìm kiếm đầu tiên của tôi mang lại trang này: http://www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx#2 mà tôi đang sử dụng làm cơ sở mã của mình. Ban đầu tôi bắt đầu như là một thiết lập cross-site, mà tôi đã thoát khỏi để xem nếu tôi chỉ có thể có được những điều làm việc. Tôi đã tìm kiếm tràn ngăn xếp và không có bài đăng nào giải quyết được vấn đề 400 Yêu cầu không hợp lệ của tôi.

Mã từ web.config của tôi:

<system.serviceModel> 
<behaviors> 
    <serviceBehaviors> 
    <behavior name="GeoDataBehavior"> 
     <serviceMetadata httpGetEnabled="true" /> 
     <serviceDebug includeExceptionDetailInFaults="true" /> 
    </behavior> 
    <behavior name=""> 
     <serviceMetadata httpGetEnabled="true" /> 
    </behavior> 
    </serviceBehaviors> 
    <endpointBehaviors> 
    <behavior name="GDEPBehavior"> 
     <webHttp /> 
    </behavior> 
    </endpointBehaviors> 
</behaviors> 
<bindings> 
    <webHttpBinding> 
    <binding name="GDBinding" crossDomainScriptAccessEnabled="true"/> 
    </webHttpBinding> 
</bindings> 
<services> 
    <service behaviorConfiguration="GeoDataBehavior" name="GeoDataService"> 
    <endpoint address="" 
       binding="webHttpBinding" contract="IGeoDataService" 
       behaviorConfiguration="GDEPBehavior"/> 
    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/> 
    </service> 
</services> 

Mã từ giao diện của tôi:

[ServiceContract] 
public interface IGeoDataService 
{ 
    [OperationContract] 
    [WebInvoke(Method = "POST", 
     BodyStyle = WebMessageBodyStyle.Wrapped, 
     ResponseFormat = WebMessageFormat.Json)] 
    List<BranchData> GetBranches(); 
} 


// Use a data contract as illustrated in the sample below to add composite types to service operations. 
[DataContract] 
public class BranchData 
{ 
    [DataMember] 
    public string BranchNumber { get; set; } 

    [DataMember] 
    public string BranchName { get; set; } 

    [DataMember] 
    public string StreetAddress { get; set; } 

    [DataMember] 
    public string City { get; set; } 

    [DataMember] 
    public string Zip { get; set; } 

    [DataMember] 
    public string State { get; set; } 

    [DataMember] 
    public string Phone { get; set; } 

    [DataMember] 
    public string County { get; set; } 
} 

jQuery kịch bản:

<script type="text/javascript" language="javascript" src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.6.1.js"> 
</script> 
<script type="text/javascript" language="javascript"> 
    /* help from http://www.codeproject.com/KB/aspnet/WCF_JQUERY_ASMX.aspx 
    */ 
    var varType; 
    var varUrl; 
    var varData; 
    var varContentType; 
    var varDataType; 
    var varProcessData; 

    function CallService() { 
     // Thank you Bing: http://blueonionsoftware.com/blog.aspx?p=03aff202-4198-4606-b9d6-686fd13697ee 
     jQuery.support.cors = true; 


     $.ajax({ 
      type: varType, 
      url: varUrl, 
      data: null, 
      crossDomain: true, 
      contentType: varContentType, 
      dataType: varDataType, 
      processdata: varProcessData, 
      success: function (msg) { 
       ServiceSucceeded(msg); 
      }, 
      error: ServiceFailed 
     }); 

     /* 
     $.getJSON(varUrl, null, function (msg) { 
      ServiceSucceeded(msg); 
     }); 
     */ 
    } 

    function GetBranchDataJson() { 
     varType = "POST"; 
     varUrl = "GeoDataService.svc/GetBranches"; 
     varData = ""; 
     varContentType = "application/json; charset=utf-8"; 
     varDataType = "json"; 
     varProcessData = true; 
     CallService(); 
    } 

    function ServiceSucceeded(result) { 
     var ddlResult = document.getElementById("ddlResult"); 
     for (var j = ddlResult.options.length - 1; j >= 0; j--) { ddlResult.remove(j); } 

     for (var i = 0; i < result.length; i++) { 
      var opt = document.createElement("option"); 
      opt.text = result[i].BranchName; 
      ddlResult.options.add(opt); 
     } 
    } 

    function ServiceFailed(jqXHR, errorType, errorThrown) { 
     alert('error!\n' + jqXHR + '\n' + errorType + '\n' + errorThrown); 
    } 

</script> 
<input name="WTF" type="button" onclick="GetBranchDataJson()" /> 

Bạn sẽ lưu ý tôi m bằng cách sử dụng jQuery 1.6.1, không phải 1.3 fr om hướng dẫn. Các hướng dẫn chạy tốt trên hộp của tôi và làm mọi thứ như mong đợi. Thật không may, mã của tôi không. Tôi đánh giá cao bất kỳ sự giúp đỡ nào mà bạn có thể cung cấp.

Oh, và đây là một bản sao của yêu cầu từ Fiddler:

POST http://localhost:16062/GeoDataService.svc/GetBranches HTTP/1.1 
Accept: application/json, text/javascript, */*; q=0.01 
Content-Type: application/json; charset=utf-8 
Referer: http://localhost:16062/Default.aspx 
Accept-Language: en-us 
Accept-Encoding: gzip, deflate 
User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0) 
Host: localhost:16062 
Content-Length: 0 
Connection: Keep-Alive 
Pragma: no-cache 

Cập nhật: Ok, tôi đã thông qua "{}" như truy vấn dữ liệu (hình như đây là cách đúng đắn để vượt qua không có gì để một phương thức không lấy tham số) và bây giờ tôi nhận được Loại phương tiện không được hỗ trợ. Ngoại lệ theo dõi là: System.ServiceModel.ProtocolException: Kiểu nội dung application/json; charset = utf-8 đã được gửi đến một dịch vụ đang chờ văn bản/xml; charset = utf-8.

+0

Vì vậy, chỉ để thử nghiệm, tôi đã thêm tham số vào phương thức GetBranches, bool Test. Tôi đã cập nhật cuộc gọi $ .ajax của mình để chuyển vào dữ liệu: '{"Test": "true"}' và bây giờ tôi gặp lỗi trong dấu vết: Content Type application/json; charset = utf-8 đã được gửi đến một dịch vụ đang chờ văn bản/xml; charset = utf-8. Tôi đã thêm ReceiveFormat = Json, nhưng vẫn gặp lỗi. Có cái gì trong web.config tôi cần phải thay đổi? Ngoài ra, là có một cách chính xác để vượt qua null để một phương pháp mà không có tham số (GetBranches ban đầu của tôi)? –

+0

Bạn có thể kiểm tra xem tên dịch vụ trong web.config có khớp với tên dịch vụ trong tệp GeoDataService.svc không? Chúng phải giống hệt nhau (ví dụ: tên lớp có không gian tên) – carlosfigueira

+0

Cảm ơn ý tưởng này, Carlos. Không gian tên của tôi là GeoDataServices và tên dịch vụ của tôi là GeoDataService. Khi tôi thay đổi tên dịch vụ trong web.config thành GeoDataServices.IGeoDataService nó ném cùng một lỗi (khi tôi thay đổi nó thành GeoDataServices.GeoDataService, nó nói với tôi sử dụng giao diện trong dấu vết). Vì vậy, một trong hai cấu hình trả về cùng một ProtocolException mới. –

Trả lời

7

Cuộc gọi của chính nó dường như không có bất kỳ vấn đề gì - bạn nên thử enable tracing để xem tại sao WCF đang xem xét yêu cầu gửi đến là xấu. Tôi đã thử một mã tương tự như một trong những bạn có (xem bên dưới) và nó đã làm việc tốt. Ngoài ra, kể từ khi yêu cầu đến từ cùng một tên miền (localhost: 16062) như dịch vụ, bạn không có bất kỳ vấn đề tên miền chéo nào.

Cập nhật: giải pháp dựa trên các chủ đề bình luận về vấn đề

Các "tên" thuộc tính của > yếu tố < dịch vụ trong web.config phải phù hợp với tên đầy đủ (tức là, không gian tên + tên) của lớp dịch vụ (nghĩa là cùng một giá trị được sử dụng trong tệp .svc). Nếu không, bạn sẽ nhận được điểm cuối mặc định được thêm cho dịch vụ của bạn, có thể hoặc không thể là thứ bạn muốn - theo mặc định bạn sẽ nhận được điểm cuối BasicHttpBinding, không phải là thứ bạn muốn trong trường hợp của mình.

Vấn đề này là tác dụng phụ không may của một tính năng được thêm trong .NET Framework 4.0: Simplified Configuration. Cho đến .NET 3.5, mọi dịch vụ cần thiết để có một mục nhập trên web.config để cấu hình nó và các tệp cấu hình cho ngay cả những ứng dụng đơn giản nhất (ví dụ, hello world) là lớn. Vì vậy, những gì đã xảy ra là, kể từ 4.0, nếu WCF không tìm thấy một phần tử dịch vụ với một tên phù hợp với tên đầy đủ của dịch vụ, nó sẽ vui vẻ nghĩ rằng bạn muốn sử dụng cấu hình mặc định.Đó là lý do tại sao nó xảy ra để "làm việc" với WcfTestClient lúc đầu.

public class StackOverflow_6526659 
{ 
    [ServiceContract] 
    public interface IGeoDataService 
    { 
     [OperationContract] 
     [WebInvoke(Method = "POST", 
      BodyStyle = WebMessageBodyStyle.Wrapped, 
      ResponseFormat = WebMessageFormat.Json)] 
     List<BranchData> GetBranches(); 
    } 

    public class Service : IGeoDataService 
    { 
     public List<BranchData> GetBranches() 
     { 
      return new List<BranchData>(); 
     } 
    } 

    // Use a data contract as illustrated in the sample below to add composite types to service operations. 
    [DataContract] 
    public class BranchData 
    { 
     [DataMember] 
     public string BranchNumber { get; set; } 

     [DataMember] 
     public string BranchName { get; set; } 

     [DataMember] 
     public string StreetAddress { get; set; } 

     [DataMember] 
     public string City { get; set; } 

     [DataMember] 
     public string Zip { get; set; } 

     [DataMember] 
     public string State { get; set; } 

     [DataMember] 
     public string Phone { get; set; } 

     [DataMember] 
     public string County { get; set; } 
    } 

    public static void Test() 
    { 
     string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; 
     ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress)); 
     WebHttpBinding binding = new WebHttpBinding { CrossDomainScriptAccessEnabled = true }; 
     WebHttpBehavior behavior = new WebHttpBehavior(); 
     host.AddServiceEndpoint(typeof(IGeoDataService), binding, "").Behaviors.Add(behavior); 
     host.Open(); 
     Console.WriteLine("Host opened"); 

     HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(baseAddress + "/GetBranches"); 
     req.Method = "POST"; 
     req.GetRequestStream().Close(); 
     HttpWebResponse resp = (HttpWebResponse)req.GetResponse(); 
     Console.WriteLine("HTTP/{0} {1} {2}", resp.ProtocolVersion, (int)resp.StatusCode, resp.StatusDescription); 
     foreach (var header in resp.Headers.AllKeys) 
     { 
      Console.WriteLine("{0}: {1}", header, resp.Headers[header]); 
     } 
     if (resp.ContentLength > 0) 
     { 
      Console.WriteLine(new StreamReader(resp.GetResponseStream()).ReadToEnd()); 
     } 

     Console.Write("Press ENTER to close the host"); 
     Console.ReadLine(); 
     host.Close(); 
    } 
} 
+0

Cảm ơn con trỏ, Carlos. Với nhật ký theo dõi, đây là lỗi: System.Xml.XmlException: Nội dung của thư không thể đọc được vì nó trống. Bây giờ, tôi không có nhiều kinh nghiệm với các cuộc gọi dịch vụ web "thô" (thông thường tôi sẽ .NET. .NET), vì vậy tôi không biết liệu đây có phải là một ngoại lệ quá chung chung để trợ giúp hay không. –

+0

Cảm ơn bạn đã làm rõ, Carlos. Không biết về WCF 4.0 so với 3.5. Một lần nữa, thực sự đánh giá cao sự giúp đỡ của bạn! –

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