2012-06-29 103 views
0

我正在爲使用WCF的Web服務編寫一個簡單的客戶端。不幸的是,Web服務只能用JSONP消息來回答,而不是普通的JSON。WCF客戶端閱讀JSONP響應

是否有可能使用.NET 4.0的內置功能來做到這一點,或者是否需要擴展其他功能以從我從服務器獲得的答案中剝離函數名稱{和}?我知道如何閱讀JSON響應,但不是JSONP。

回答

1

你需要的是自定義消息編碼器。在服務器端,它是將填充(函數調用)添加到響應的編碼器,因此在處理消息(可能將其委託給另一個編碼器)之前,您需要在客戶端上類似的東西來刪除該填充。在編碼器中需要擔心的另一件事是,用於JSONP(application/x-javascript)的內容類型通常不被識別爲JSON內容類型(因爲它不是,它是函數調用),因此編碼器還應該將該內容類型「翻譯」爲被該呼叫委託給的編碼器所理解的內容類型。

下面的代碼顯示了這樣的編碼器的一個例子。服務已被修改爲始終包裝結果,正如您提到的服務所做的那樣。

public class StackOverflow_11255528 
{ 
    [ServiceContract] 
    public interface ICalculator 
    { 
     [WebGet(ResponseFormat = WebMessageFormat.Json)] 
     int Add(int x, int y); 
     [WebGet(ResponseFormat = WebMessageFormat.Json)] 
     int Subtract(int x, int y); 
    } 
    [ServiceContract] 
    public class CalculatorService 
    { 
     [WebGet(ResponseFormat = WebMessageFormat.Json)] 
     public Stream Add(int x, int y) 
     { 
      return ReturnWrapped(x + y); 
     } 

     [WebGet(ResponseFormat = WebMessageFormat.Json)] 
     public Stream Subtract(int x, int y) 
     { 
      return ReturnWrapped(x - y); 
     } 

     private Stream ReturnWrapped(int result) 
     { 
      string callback = "Something"; 
      string response = string.Format("{0}({1});", callback, result); 
      WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-javascript"; 
      return new MemoryStream(Encoding.UTF8.GetBytes(response)); 
     } 
    } 
    public class JsonpAwareClientMessageEncodingBindingElement : MessageEncodingBindingElement 
    { 
     WebMessageEncodingBindingElement webEncoding; 

     public JsonpAwareClientMessageEncodingBindingElement() 
     { 
      this.webEncoding = new WebMessageEncodingBindingElement(); 
     } 

     public override MessageEncoderFactory CreateMessageEncoderFactory() 
     { 
      return new JsonpAwareClientMessageEncoderFactory(this.webEncoding.CreateMessageEncoderFactory()); 
     } 

     public override MessageVersion MessageVersion 
     { 
      get { return this.webEncoding.MessageVersion; } 
      set { this.webEncoding.MessageVersion = value; } 
     } 

     public override BindingElement Clone() 
     { 
      return new JsonpAwareClientMessageEncodingBindingElement(); 
     } 

     public override IChannelFactory<TChannel> BuildChannelFactory<TChannel>(BindingContext context) 
     { 
      context.BindingParameters.Add(this); 
      return context.BuildInnerChannelFactory<TChannel>(); 
     } 

     class JsonpAwareClientMessageEncoderFactory : MessageEncoderFactory 
     { 
      private MessageEncoderFactory factory; 

      public JsonpAwareClientMessageEncoderFactory(MessageEncoderFactory factory) 
      { 
       this.factory = factory; 
      } 

      public override MessageEncoder Encoder 
      { 
       get { return new JsonpAwareClientMessageEncoder(this.factory.Encoder); } 
      } 

      public override MessageVersion MessageVersion 
      { 
       get { return this.factory.MessageVersion; } 
      } 
     } 

     class JsonpAwareClientMessageEncoder : MessageEncoder 
     { 
      private MessageEncoder encoder; 

      public JsonpAwareClientMessageEncoder(MessageEncoder encoder) 
      { 
       this.encoder = encoder; 
      } 

      public override string ContentType 
      { 
       get { return this.encoder.ContentType; } 
      } 

      public override string MediaType 
      { 
       get { return this.encoder.MediaType; } 
      } 

      public override MessageVersion MessageVersion 
      { 
       get { return this.encoder.MessageVersion; } 
      } 

      public override bool IsContentTypeSupported(string contentType) 
      { 
       if (contentType == "application/x-javascript") 
       { 
        contentType = "application/json"; 
       } 

       return this.encoder.IsContentTypeSupported(contentType); 
      } 

      public override Message ReadMessage(ArraySegment<byte> buffer, BufferManager bufferManager, string contentType) 
      { 
       if (contentType == "application/x-javascript") 
       { 
        contentType = "application/json"; 
       } 

       byte openParenthesis = (byte)'('; 
       byte closeParenthesis = (byte)')'; 
       int startOfParenthesis = buffer.Offset; 
       int count = buffer.Count; 
       while (buffer.Array[startOfParenthesis] != openParenthesis) 
       { 
        startOfParenthesis++; 
        count--; 
       } 

       // Skipped 'Func', now skipping '(' 
       startOfParenthesis++; 
       count--; 

       // Now need to trim the closing parenthesis and semicolon, if any 
       int endOfParenthesis = buffer.Offset + buffer.Count - 1; 
       while (buffer.Array[endOfParenthesis] != closeParenthesis) 
       { 
        endOfParenthesis--; 
        count--; 
       } 

       // Skipped back to ')', now remove it 
       endOfParenthesis--; 
       count--; 

       return this.encoder.ReadMessage(new ArraySegment<byte>(buffer.Array, startOfParenthesis, count), bufferManager, contentType); 
      } 

      public override Message ReadMessage(Stream stream, int maxSizeOfHeaders, string contentType) 
      { 
       throw new NotSupportedException("Streamed mode not supported"); 
      } 

      public override ArraySegment<byte> WriteMessage(Message message, int maxMessageSize, BufferManager bufferManager, int messageOffset) 
      { 
       return this.encoder.WriteMessage(message, maxMessageSize, bufferManager, messageOffset); 
      } 

      public override void WriteMessage(Message message, Stream stream) 
      { 
       throw new NotSupportedException("Streamed mode not supported"); 
      } 
     } 
    } 
    public static void Test() 
    { 
     string baseAddress = "http://" + Environment.MachineName + ":8000/Service"; 
     ServiceHost host = new ServiceHost(typeof(CalculatorService), new Uri(baseAddress)); 
     WebHttpBinding binding = new WebHttpBinding { CrossDomainScriptAccessEnabled = true }; 
     host.AddServiceEndpoint(typeof(CalculatorService), binding, "").Behaviors.Add(new WebHttpBehavior()); 
     host.Open(); 
     Console.WriteLine("Host opened"); 

     WebClient c = new WebClient(); 
     Console.WriteLine(c.DownloadString(baseAddress + "/Add?x=5&y=8&callback=Func")); 

     CustomBinding clientBinding = new CustomBinding(
      new JsonpAwareClientMessageEncodingBindingElement(), 
      new HttpTransportBindingElement { ManualAddressing = true }); 
     ChannelFactory<ICalculator> factory = new ChannelFactory<ICalculator>(clientBinding, new EndpointAddress(baseAddress)); 
     factory.Endpoint.Behaviors.Add(new WebHttpBehavior()); 
     ICalculator proxy = factory.CreateChannel(); 
     Console.WriteLine(proxy.Subtract(456, 432)); 

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

有沒有直接的方法,我知道,但讓我們先試着去理解

//JSON 
{"prop":"val"} 
//JSONP 
func({"prop":"val"}); 

一個JSON和JSONP之間的差異要獲得JSON字符串你可以簡單地剝離的每一件事情之間的「(」和「 )「括號,然後使用不同的JSON庫將其轉換爲對象。

string jsonString = Regex.Match(jsonpString, @"\(([^)]*)\)").Groups[1].Value 
+0

我知道......但是WCF的哪個部分需要擴展才能實現我自己的反序列化類? –