2012-06-29 7 views
0

WCF를 사용하는 웹 서비스에 대한 간단한 클라이언트를 작성하고 있습니다. 불행히도 웹 서비스는 JSONP 메시지 만 사용하여 응답하고 일반 JSON은 응답하지 않습니다.WCF JSONP 응답을 읽는 클라이언트

이렇게하려면 .NET 4.0의 기본 제공 기능을 사용할 수 있습니까? 아니면 서버에서 가져온 응답에서 함수 이름을 제거하기 위해 뭔가를 확장해야합니까? JSONP는 읽지 않지만 JSONP는 읽는 방법을 알고 있습니다.

답변

1

사용자 정의 메시지 인코더가 필요합니다. 서버 측에서는 패딩 (함수 호출)을 응답에 추가하는 인코더이므로 메시지를 처리하기 전에 패딩을 제거하기 위해 클라이언트 측에서 비슷한 것을 필요로합니다 (다른 인코더로 위임 할 가능성이 있음). 인코더에서 걱정할 필요가있는 다른 사항은 JSONP (응용 프로그램/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 문자열을 얻으려면

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

JSON과 JSONP의 차이를 이해하려고 노력하자 직접적인 방법이없는 "("와 ") "중괄호를 사용하고 다른 JSON 라이브러리를 사용하여 객체로 변환합니다.

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

알아요.하지만 내 자체 역 직렬화 클래스를 구현하려면 WCF의 어느 부분을 확장해야합니까? –

관련 문제