WCF客户端读取JSONP响应

时间:2012-06-29 03:04:24

标签: wcf jsonp

我正在为使用WCF的Web服务编写一个简单的客户端。不幸的是,Web服务只回答JSONP消息,而不是普通的JSON。

是否可以使用.NET 4.0中的内置功能来执行此操作,还是需要扩展其他内容以从我从服务器获得的答案中删除函数名称{和}?我知道如何阅读JSON响应,但还不知道JSONP。

2 个答案:

答案 0 :(得分: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();
    }
}

答案 1 :(得分:0)

我知道没有直接的方法,但我们首先尝试了解JSON和JSONP之间的区别

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

要获取JSON字符串,您可以简单地删除“(”和“)”括号之间的所有内容,然后使用不同的JSON库将其转换为对象。

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