3

我正在尝试在 WCF 中实现一个 UPnP MediaServer。我正在慢慢到达那里,但现在我碰上了一堵砖墙。我需要为 ServiceContract 命名空间添加一个前缀。现在我有以下内容:

[ServiceContract(Namespace = "urn:schemas-upnp-org:service:ContentDirectory:1")]
public interface  IContentDirectory
{
    [OperationContract(Action = "urn:schemas-upnp-org:service:ContentDirectory:1#Browse")]
    void Browse(string ObjectID, string BrowseFlag, string Filter, ulong StartingIndex, ulong RequestedCount, string SortCriteria, out string Result, out ulong NumberReturned, out ulong TotalMatches, out ulong UpdateID);
}

这会听取正确的肥皂信息。但是,我需要肥皂体开始

<u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">

WCF 现在正在收听:

<Browse xmlns="urn:schemas-upnp-org:service:ContentDirectory:1">

我如何在那里获得前缀?有关系吗?或者还有其他原因导致参数没有传递到 Browse 方法中?

更新 这里有一些额外的信息:下面的消息是由一个真正的 UPnP 控制点发送的。参数不会传递到 Browse 方法中。

<s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://XXXXX:8731/ContentDirectory</To>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">urn:schemas-upnp-org:service:ContentDirectory:1#Browse</Action>
  </s:Header>
  <s:Body>
      <u:Browse xmlns:u="urn:schemas-upnp-org:service:ContentDirectory:1">
         <ObjectID>0</ObjectID>
         <BrowseFlag>BrowseDirectChildren</BrowseFlag>
         <Filter>*</Filter>
         <StartingIndex>0</StartingIndex>
         <RequestedCount>15</RequestedCount>
         <SortCriteria />
      </u:Browse>
   </s:Body>
</s:Envelope>

这是由 WCF 测试客户端生成的请求。现在参数被传递到 Browse 方法中:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <To s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://XXXXXX:8731/ContentDirectory</To>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">urn:schemas-upnp-org:service:ContentDirectory:1#Browse</Action>
  </s:Header>
  <s:Body>
    <Browse xmlns="urn:schemas-upnp-org:service:ContentDirectory:1">
      <ObjectID>0</ObjectID>
      <BrowseFlag>BrowseMetadata</BrowseFlag>
      <Filter>*</Filter>
      <StartingIndex>0</StartingIndex>
      <RequestedCount>0</RequestedCount>
      <SortCriteria i:nil="true" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" />
    </Browse>
  </s:Body>
</s:Envelope>
4

2 回答 2

4

您显示的两个请求(一个来自 UPnP 控制点,一个来自 WCF 测试客户端)是不等价的。第一个 (UPnP) 中 Browse 元素的子元素的命名空间是空 ("") 命名空间,而在第二个请求 (WCF TC) 中它是 "urn:schemas-upnp-org:service:ContentDirectory:1" -空前缀绑定到空命名空间,除非它绑定到另一个命名空间,并且它会在 WCF 请求中反弹,但不会在 UPnP 请求中反弹。

现在,如果您希望操作参数的名称空间与操作本身的名称空间不同,则必须使用消息协定——这将使 WCF 发送的请求等效(模前缀) UPnP 控制点发送的请求。下面的代码显示了如何定义消息契约以生成与您为控制点显示的请求等效的请求。

public class StackOverflow_2495195
{
    [MessageContract(WrapperName="Browse", WrapperNamespace="urn:schemas-upnp-org:service:ContentDirectory:1", IsWrapped=true)]
    public class BrowseRequest
    {
        [MessageBodyMember(Namespace = "", Order = 0)]
        public string ObjectID;

        [MessageBodyMember(Namespace = "", Order = 1)]
        public string BrowseFlag;

        [MessageBodyMember(Namespace = "", Order = 2)]
        public string Filter;

        [MessageBodyMember(Namespace = "", Order = 3)]
        public ulong StartingIndex;

        [MessageBodyMember(Namespace = "", Order = 4)]
        public ulong RequestedCount;

        [MessageBodyMember(Namespace = "", Order = 5)]
        public string SortCriteria;
    }

    [MessageContract(WrapperName = "BrowseResponse", WrapperNamespace = "urn:schemas-upnp-org:service:ContentDirectory:1", IsWrapped = true)]
    public class BrowseResponse
    {
        [MessageBodyMember(Namespace = "", Order = 0)]
        public string Result;

        [MessageBodyMember(Namespace = "", Order = 1)]
        public ulong NumberReturned;

        [MessageBodyMember(Namespace = "", Order = 2)]
        public ulong TotalMatches;

        [MessageBodyMember(Namespace = "", Order = 3)]
        public ulong UpdateID;
    }

    [ServiceContract(Namespace = "urn:schemas-upnp-org:service:ContentDirectory:1")]
    public interface IContentDirectory
    {
        [OperationContract(Action = "urn:schemas-upnp-org:service:ContentDirectory:1#Browse")]
        //void Browse(string ObjectID, string BrowseFlag, string Filter, ulong StartingIndex, ulong RequestedCount, string SortCriteria, out string Result, out ulong NumberReturned, out ulong TotalMatches, out ulong UpdateID);
        BrowseResponse Browse(BrowseRequest request);
    }
    public class Service : IContentDirectory
    {
        //public void Browse(string ObjectID, string BrowseFlag, string Filter, ulong StartingIndex, ulong RequestedCount, string SortCriteria, out string Result, out ulong NumberReturned, out ulong TotalMatches, out ulong UpdateID)
        //{
        //    Result = null;
        //    NumberReturned = 0;
        //    TotalMatches = 0;
        //    UpdateID = 0;
        //}
        public BrowseResponse Browse(BrowseRequest request)
        {
            return new BrowseResponse { NumberReturned = 0, Result = null, TotalMatches = 0, UpdateID = 0 };
        }
    }
    static Binding GetBinding()
    {
        return new CustomBinding(
            new TextMessageEncodingBindingElement(MessageVersion.Soap11WSAddressing10, Encoding.UTF8),
            new HttpTransportBindingElement());
    }
    public static void Test()
    {
        string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
        ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
        host.AddServiceEndpoint(typeof(IContentDirectory), GetBinding(), "");
        host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
        host.Open();
        Console.WriteLine("Host opened");

        ChannelFactory<IContentDirectory> factory = new ChannelFactory<IContentDirectory>(GetBinding(), new EndpointAddress(baseAddress));
        IContentDirectory proxy = factory.CreateChannel();
        //string result;
        //ulong ul1, ul2, ul3;
        //proxy.Browse(null, null, null, 0, 0, null, out result, out ul1, out ul2, out ul3);
        proxy.Browse(new BrowseRequest { BrowseFlag = null, Filter = null, ObjectID = null, RequestedCount = 0, SortCriteria = null, StartingIndex = 0 });

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

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

现在,WCF 发送的请求是 XML 等效的,但前缀仍然不同。正如 John Saunders 所提到的,这无关紧要,因此 WCF 中没有简单的旋钮可以让您为 WCF 消息中写入的元素设置前缀。但是,您可以使用自定义编码器来做到这一点。我不久前在http://blogs.msdn.com/b/carlosfigueira/archive/2010/06/13/changeing-prefixes-in-xml-responses.aspx上发布了它- 您可以使用该示例创建一个自定义编码器,它将前缀设置为您期望的前缀。

于 2011-06-27T23:38:35.780 回答
1

为什么需要特定的前缀?您是否正在使用一些依赖于前缀的软件?

如果是这样,那么软件坏了。就 XML 而言,您给出的两个“xmlns”示例是相同的。任何关心差异的软件都严重损坏,需要学习 XML。

于 2010-03-22T19:48:48.753 回答