0

我正在尝试使用 WCF 来使用第 3 方 REST 服务,该服务使用 URL 编码的数据进行响应:

a=1&b=2&c=3

我现在有这个:

[DataContract]
class Response { 
  [DataMember(Name="a")]
  public int A { get;set;}
  [DataMember(Name="b")]
  public int B { get;set;}
  [DataMember(Name="c")]
  public int C { get;set;}
}

[ServiceContract]
interface IService
{
  [OperationContract]
  Response Foo();
}

但它回来了:

There was an error checking start element of object of type Response. The data at the root level is invalid. Line 1, position 1.
4

1 回答 1

1

WCF 不“理解”表单数据内容类型(application/x-www-forms-urlencoded),因此它无法直接读取该响应。您可以实现一个消息格式化程序,它将能够将该格式转换为您的合同,或者您可以接收作为 a 的响应Stream(这将为您提供响应的完整字节),您可以将其解码到您的类中。

下面的代码显示了如何为此操作实现格式化程序。它不是通用的,但您应该了解需要做什么。

public class StackOverflow_16493746
{
    [ServiceContract]
    public class Service
    {
        [WebGet(UriTemplate = "*")]
        public Stream GetData()
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "application/x-www-form-urlencoded";
            return new MemoryStream(Encoding.UTF8.GetBytes("a=1&b=2&c=3"));
        }
    }
    [ServiceContract]
    interface IService
    {
        [WebGet]
        Response Foo();
    }
    [DataContract]
    class Response
    {
        [DataMember(Name = "a")]
        public int A { get; set; }
        [DataMember(Name = "b")]
        public int B { get; set; }
        [DataMember(Name = "c")]
        public int C { get; set; }
    }
    public class MyResponseFormatter : IClientMessageFormatter
    {
        private IClientMessageFormatter originalFormatter;

        public MyResponseFormatter(IClientMessageFormatter originalFormatter)
        {
            this.originalFormatter = originalFormatter;
        }

        public object DeserializeReply(Message message, object[] parameters)
        {
            HttpResponseMessageProperty httpResp = (HttpResponseMessageProperty)
                message.Properties[HttpResponseMessageProperty.Name];
            if (httpResp.Headers[HttpResponseHeader.ContentType] == "application/x-www-form-urlencoded")
            {
                if (parameters.Length > 0)
                {
                    throw new InvalidOperationException("out/ref parameters not supported in this formatter");
                }

                byte[] bodyBytes = message.GetReaderAtBodyContents().ReadElementContentAsBase64();
                NameValueCollection pairs = HttpUtility.ParseQueryString(Encoding.UTF8.GetString(bodyBytes));
                Response result = new Response();
                foreach (var key in pairs.AllKeys)
                {
                    string value = pairs[key];
                    switch (key)
                    {
                        case "a":
                            result.A = int.Parse(value);
                            break;
                        case "b":
                            result.B = int.Parse(value);
                            break;
                        case "c":
                            result.C = int.Parse(value);
                            break;
                    }
                }

                return result;
            }
            else
            {
                return this.originalFormatter.DeserializeReply(message, parameters);
            }
        }

        public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
        {
            throw new NotSupportedException("This is a reply-only formatter");
        }
    }
    public class MyClientBehavior : WebHttpBehavior
    {
        protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
        {
            return new MyResponseFormatter(base.GetReplyClientFormatter(operationDescription, endpoint));
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        WebServiceHost host = new WebServiceHost(typeof(Service), new Uri(baseAddress));
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<IService> factory = new ChannelFactory<IService>(new WebHttpBinding(), new EndpointAddress(baseAddress));
        factory.Endpoint.Behaviors.Add(new MyClientBehavior());
        IService proxy = factory.CreateChannel();
        Response resp = proxy.Foo();
        Console.WriteLine("a={0},b={1},c={2}", resp.A, resp.B, resp.C);

        ((IClientChannel)proxy).Close();
        factory.Close();

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
于 2013-05-11T16:01:11.690 回答