使用我的WCF HTTP REST服务,如何支持带或不带命名空间的XML

时间:2011-09-21 20:16:50

标签: xml wcf serialization

我有一系列看起来像这样的对象:

namespace MyNamespace
{
  [DataContract(Namespace="")]
  public class MyClass1
  {
    [DataMember]
    public string MyProperty {get;set;}
  }
}

我有一个暴露WebInvoke的方法,看起来像这样(非常简化,因为它现在实际上什么也没做,但仍适用于此测试)

[WebInvoke(UriTemplate = "", Method="POST")]
public MyNamespace.MyClass1 GetItem(MyClass1 postedItem) { return postedItem; }

我真的希望能够接受如下所示的XML:

<MyClass1>
  <MyProperty>1</MyProperty>
</MyClass1>

或者这个:

<MyClass1 xmlns:"http://schemas.datacontract.org/2004/07/MyNamespace">
  <MyProperty>1</MyProperty>
</MyClass1>

但到目前为止,我的研究似乎表明这是不可能的。我现在唯一的想法是使用IDispatchMessageInspector并使用消息,删除xmlns命名空间,然后允许WCF继续处理消息。然而,我没有太多运气,因为一旦我使用了消息,它就不再可供WCF使用和反序列化了。

有更简单的方法吗?还有更好的方法吗?

4 个答案:

答案 0 :(得分:1)

您可以使用调度程序,但是一旦使用了消息,就需要在从方法返回之前重新创建它。下面的代码显示了它的一个例子。

public class StackOverflow_7506072
{
    [DataContract(Name = "MyClass1", Namespace = "")]
    public class MyClass1
    {
        [DataMember]
        public string MyProperty { get; set; }
    }
    [ServiceContract]
    public class Service
    {
        [WebInvoke(UriTemplate = "", Method = "POST")]
        public MyClass1 GetItem(MyClass1 postedItem) { return postedItem; }
    }
    public class MyInspector : IEndpointBehavior, IDispatchMessageInspector
    {
        public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
        {
        }

        public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
        {
            endpointDispatcher.DispatchRuntime.MessageInspectors.Add(this);
        }

        public void Validate(ServiceEndpoint endpoint)
        {
        }

        public object AfterReceiveRequest(ref Message request, IClientChannel channel, InstanceContext instanceContext)
        {
            MemoryStream ms = new MemoryStream();
            XmlWriter w = XmlWriter.Create(ms);
            request.WriteMessage(w);
            w.Flush();
            ms.Position = 0;
            XElement element = XElement.Load(ms);
            if (element.Name.NamespaceName == "http://schemas.datacontract.org/2004/07/MyNamespace")
            {
                element.Name = XName.Get(element.Name.LocalName, "");
                foreach (XElement child in element.Descendants())
                {
                    if (child.Name.NamespaceName == "http://schemas.datacontract.org/2004/07/MyNamespace")
                    {
                        child.Name = XName.Get(child.Name.LocalName, "");
                    }
                }

                element.Attribute("xmlns").Remove();
            }

            XmlReader r = element.CreateReader();
            Message newRequest = Message.CreateMessage(r, int.MaxValue, request.Version);
            newRequest.Properties.CopyProperties(request.Properties);
            request = newRequest;
            return null;
        }

        public void BeforeSendReply(ref Message reply, object correlationState)
        {
        }
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        ServiceEndpoint endpoint = host.AddServiceEndpoint(typeof(Service), new WebHttpBinding(), "");
        endpoint.Behaviors.Add(new WebHttpBehavior());
        endpoint.Behaviors.Add(new MyInspector());
        host.Open();
        Console.WriteLine("Host opened");

        WebClient c = new WebClient();
        c.Headers[HttpRequestHeader.ContentType] = "text/xml";
        string xml = "<MyClass1><MyProperty>123</MyProperty></MyClass1>";
        Console.WriteLine(c.UploadString(baseAddress + "/", xml));

        c = new WebClient();
        c.Headers[HttpRequestHeader.ContentType] = "text/xml";
        xml = "<MyClass1 xmlns=\"http://schemas.datacontract.org/2004/07/MyNamespace\"><MyProperty>123</MyProperty></MyClass1>";
        Console.WriteLine(c.UploadString(baseAddress + "/", xml));

        Console.Write("Press ENTER to close the host");
        Console.ReadLine();
        host.Close();
    }
}

答案 1 :(得分:0)

这是一个可怕的想法。您正在将XML视为标准并不重要。

MyClass1命名空间中的元素"http://schemas.datacontract.org/2004/07/MyNamespace"与默认命名空间中的元素MyClass不同。

答案 2 :(得分:0)

您还可以使用WcfRestContrib注入支持此行为的自定义XmlSerializer。使用始终完全省略名称空间的XmlSerializer的示例是here。您需要了解如何更新XmlSerializer以选择性地支持名称空间以进行反序列化。

答案 3 :(得分:0)

如果将Servicespace的Namespace设置为Empty,则无需传递xmlns。但是你需要与方法名称匹配的根xml标签,即

<GetItem>
<MyClass1>
  <MyProperty>1</MyProperty>
</MyClass1>
</GetItem>