0

WCF を使用して Web サービスの単純なクライアントを作成しています。残念ながら、Web サービスはプレーンな JSON ではなく、JSONP メッセージでのみ応答します。

これを行うために .NET 4.0 の組み込み機能を使用することは可能ですか、それともサーバーから取得した応答から関数名 { および } を削除するために何か他のものを拡張する必要がありますか? JSON 応答の読み方は知っていますが、まだ JSONP は知りません。

4

2 に答える 2

1

必要なのは、カスタム メッセージ エンコーダーです。サーバー側では、応答にパディング (関数呼び出し) を追加するのはエンコーダーであるため、メッセージを処理する前にそのパディングを削除するために、クライアント側で同様のものが必要です (別のエンコーダーに委任する可能性があります)。エンコーダーで心配する必要があるもう 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();
    }
}
于 2012-07-06T00:09:02.547 に答える
0

私が知っている直接的な方法はありませんが、最初に JSON と JSONP の違いを理解してみましょう

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

JSON 文字列を取得するには、"(" と ")" の中かっこの間を単純にすべて取り除き、別の JSON ライブラリを使用してそれをオブジェクトに変換します。

string jsonString = Regex.Match(jsonpString, @"\(([^)]*)\)").Groups[1].Value
于 2012-06-29T06:22:29.007 に答える