1

无论内容类型如何,如何强制 WCF Rest 客户端使用 Json 反序列化器?

我正在通过 WCF 调用基于 REST 的 Web 服务。

该服务返回 JSON 正文,但具有内容类型“Application/xml”。WCF 框架现在给了我 XmlException。

public class MessageFormatter : IClientMessageFormatter
{
    private readonly IClientMessageFormatter _formatter;

    public MessageFormatter(IClientMessageFormatter formatter)
    {
        _formatter = formatter;
    }

    public object DeserializeReply(System.ServiceModel.Channels.Message message, object[] parameters)
    {
        return _formatter.DeserializeReply(message, parameters);
    }
}

_formatter.DeserializeReply 正在抛出 XmlException。我在任何地方都找不到任何示例来强制对回复进行 json 反序列化。

编辑-鼠标悬停时的“消息”对象抛出“{...错误读取正文:System.Xml.XmlException:根级别的数据无效。第1行,位置1....}”

我的另一个项目中与不同的 REST 服务(Picasa Web 服务)通信的同一个对象有一个看起来像 JSON 对象的 xml 序列化版本的东西?所以这个问题似乎更进一步。我需要找到这个对象的来源。我会玩 MessageEncoder 类。

编辑 - (添加更多信息)

public class MyBinding : WebHttpBinding
{
    public MyBinding(WebHttpSecurityMode mode)
        : base(mode)
    {

    }

    public override BindingElementCollection CreateBindingElements()
    {
        var result = base.CreateBindingElements();

        var replacements = result.OfType<MessageEncodingBindingElement>().ToList();
        foreach (var messageEncodingBindingElement in replacements)
        {
            var index = result.IndexOf(messageEncodingBindingElement);
            result.Remove(messageEncodingBindingElement);
            result.Insert(index, new MyMessageEncodingBindingElement(messageEncodingBindingElement));
        }

        return result;
    }
}

public class MyMessageEncodingBindingElement : MessageEncodingBindingElement
{
    private readonly MessageEncodingBindingElement _element;

    public MyMessageEncodingBindingElement(MessageEncodingBindingElement element)
    {
        _element = element;
    }

    public override BindingElement Clone()
    {
        var result = _element.Clone();

        if (result is MessageEncodingBindingElement)
            return new MyMessageEncodingBindingElement(result as MessageEncodingBindingElement);

        return result;
    }

    public override MessageEncoderFactory CreateMessageEncoderFactory()
    {
        return new MyMessageEncoderFactory(_element.CreateMessageEncoderFactory());
    }
}

即使在设置断点时命中构造函数和 Clone 方法,也永远不会调用 CreateMessageEncoderFactory() 方法。有什么帮助吗?我正在尝试设置自定义 MessageEncoder 和 MessageEncoderFactory 类来修改 Message 对象的实例化过程。

4

3 回答 3

3

你可以使用 a WebContentTypeMapper。这是 的一个属性WebHttpBinding,您可以自定义编码器如何从该绑定中完成反序列化,包括强制它始终使用 JSON 反序列化器,而不管传入消息的 Content-Type。下面的代码显示了如何做到这一点。

public class StackOverflow_13225272
{
    [DataContract]
    public class Person
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public int Age { get; set; }

        public override string ToString()
        {
            return string.Format("Person[Name={0},Age={1}]", Name, Age);
        }
    }
    [ServiceContract]
    public interface ITest
    {
        [WebGet(ResponseFormat = WebMessageFormat.Json)]
        Person GetPerson(string responseContentType);
    }

    public class Service : ITest
    {
        public Person GetPerson(string responseContentType)
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = responseContentType;
            return new Person { Name = "John Doe", Age = 29 };
        }
    }
    class AllJsonContentTypeMapper : WebContentTypeMapper
    {
        public override WebContentFormat GetMessageFormatForContentType(string contentType)
        {
            return WebContentFormat.Json;
        }
    }
    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");

#if USE_NETFX4
        // This works on .NET 4.0 and beyond
        WebHttpBinding binding = new WebHttpBinding();
        binding.ContentTypeMapper = new AllJsonContentTypeMapper();
#else
        // This works on .NET 3.5
        CustomBinding binding = new CustomBinding(new WebHttpBinding());
        binding.Elements.Find<WebMessageEncodingBindingElement>().ContentTypeMapper = new AllJsonContentTypeMapper();
        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(binding, new EndpointAddress(baseAddress));
#endif

        ChannelFactory<ITest> factory = new ChannelFactory<ITest>(binding, new EndpointAddress(baseAddress));
        factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
        ITest proxy = factory.CreateChannel();

        Console.WriteLine("With JSON: {0}", proxy.GetPerson("application/json"));
        Console.WriteLine("With XML: {0}", proxy.GetPerson("application/xml"));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}
于 2012-11-05T16:15:05.233 回答
1

这可能会奏效。

public class ForceJsonClientMessageFormatter : IClientMessageFormatter
{
    private readonly DataContractJsonSerializer _jsonSerializer;

    public ForceJsonClientMessageFormatter(Type responseType)
    {
        _jsonSerializer = new DataContractJsonSerializer(responseType);
    }

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
    {
        throw new NotImplementedException("This client message formatter is for replies only!");
    }

    public object DeserializeReply(Message message, object[] parameters)
    {
        string messageBody = message.GetBody<string>();

        using (MemoryStream messageStream = new MemoryStream(Encoding.UTF8.GetBytes(messageBody)))
        {
            messageStream.Seek(0, SeekOrigin.Begin);
            object deserializedObject = _jsonSerializer.ReadObject(messageStream);
            return deserializedObject;
        }
    }
}

public class ForceJsonWebHttpBehavior : WebHttpBehavior
{
    protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
    {
        return new ForceJsonClientMessageFormatter(operationDescription.Messages[1].Body.ReturnValue.Type);
    }
}
于 2012-11-05T04:52:44.660 回答
0

我还没有尝试过,但我认为这会奏效。您可以创建一个自定义IClientMessageFormatter,它将消息格式覆盖为 Json,将其包装在一个行为中,然后将该行为应用于您的客户端端点配置。

public class ForceJsonClientMessageFormatterDecorator : IClientMessageFormatter
{
    private readonly IClientMessageFormatter _decoratedFormatter;
    public ForceJsonClientMessageFormatterDecorator(IClientMessageFormatter decoratedFormatter)
    {
        _decoratedFormatter = decoratedFormatter;
    }

    public object DeserializeReply(Message message, object[] parameters)
    {
        message.Properties[WebBodyFormatMessageProperty.Name] = new WebBodyFormatMessageProperty(WebContentFormat.Json);
        return _decoratedFormatter.DeserializeReply(message, parameters);
    }

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters)
    {
        return _decoratedFormatter.SerializeRequest(messageVersion, parameters);
    }
}

public class ForceJsonWebHttpBehavior : WebHttpBehavior
{
    protected override IClientMessageFormatter GetReplyClientFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
    {
        IClientMessageFormatter decoratedFormatter = base.GetReplyClientFormatter(operationDescription, endpoint);
        return new ForceJsonClientMessageFormatterDecorator(decoratedFormatter);
    }
}
于 2012-11-05T03:23:40.727 回答