gpt4 book ai didi

c# - WCF DataContracts - 如何将单个 DataContract 与复杂对象同时用于 WCF SOAP 和 REST 服务?

转载 作者:太空宇宙 更新时间:2023-11-03 11:07:30 25 4
gpt4 key购买 nike

我有一个令人沮丧的问题,我一直在努力克服但似乎无法弄清楚。

我有通过 WCF 中的 SOAP 和 REST 端点公开的服务。为了避免重复的对象代码,我想在两个服务之间重用契约(Contract)对象。仅供引用,我使用两个单独的接口(interface),因为我有许多 API 调用在两个端点之间是不同的。这些服务的一个简单示例如下所示:

/*REST Implementation*/
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke]
TestResponse Test(TestRequest request);

[OperationContract]
[WebGet]
int GetTest(int testId);

}

/*SOAP Implementation*/
[ServiceContract]
public interface ITestService
{
[OperationContract]
[WebInvoke]
TestResponse Test(TestRequest request);

[OperationContract]
[WebInvoke]
int GetTest(GetTestRequest request);
}

[DataContract(Namespace="http://www.mysite.com/Test")]
public class TestRequest
{
[DataMember]
public int ID {get;set;}

[DataMember]
public InnerTestRequest InnerTestRequest {get;set;}
}

[DataContract(Namespace="http://www.mysite.com/Test")]
public class InnerTestRequest
{
[DataMember]
public int ID {get;set;}
}

问题 我遇到的问题是我希望两个端点的契约(Contract)负载使用相同的 XML 结构(在 SOAP 端点的 SOAP 信封内)。

例如,调用 REST 端点上的 Test(TestRequest request),我想发送以下 XML:

 <TestRequest xmlns="http://www.mysite.com/Test">
<InnerTestRequest>
<ID>2</ID>
</InnerTestRequest>
<ID>4</ID>
</TestRequest>

对于 SOAP 端点,我希望能够发送以下内容:

 <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<TestRequest xmlns="http://www.mysite.com/Test">
<InnerTestRequest>
<ID>2</ID>
</InnerTestRequest>
<ID>4</ID>
</TestRequest>
</s:Body>

我还希望响应具有相同的格式和相同的契约(Contract)负载。我尝试了多种方法来实现这一点,包括使用 [MessageContractAttribute] 和指定命名空间,以及将 BodyStyle 设置为 BodyStyle.Bare,但我仍然遇到以下两个问题:

1. The http://www.mysite.com/Test namespace does not trickle down to the members of its class.
2. SOAP requests "wrap" the contract, and it changes the structure of the XML.

在不指定两个单独的 DataContracts(一个用于 REST,一个用于 SOAP)的情况下实现这一目标的最佳方法是什么。

提前致谢

最佳答案

对于第一项:您还需要将 [OperationContract] 命名空间定义为数据协定中的相同命名空间,这样您就有了一个一致的命名空间故事。

对于第二个项目,您在消息契约(Contract)方面走在了正确的轨道上。如果您想删除“包装”元素,则需要使用未包装 消息协定。

下面的代码展示了这是如何实现的。

public class StackOverflow_15252991
{
[DataContract(Name = "TestRequest", Namespace = "http://www.mysite.com/Test")]
public class TestRequest
{
[DataMember(Order = 2)]
public int ID { get; set; }

[DataMember(Order = 1)]
public InnerTestRequest InnerTestRequest { get; set; }
}

[DataContract(Name = "InnerTestRequest", Namespace = "http://www.mysite.com/Test")]
public class InnerTestRequest
{
[DataMember]
public int ID { get; set; }
}

[DataContract(Namespace = "http://www.mysite.com/Test", Name = "TestResponse")]
public class TestResponse
{
[DataMember]
public int ID { get; set; }
}

[ServiceContract(Namespace = "http://www.mysite.com/Test")]
public interface ITestService
{
[OperationContract]
[WebInvoke]
TestResponseContract Test(TestRequestContract request);
}
[MessageContract(IsWrapped = false)]
public class TestRequestContract
{
[MessageBodyMember]
public TestRequest TestRequest { get; set; }
}
[MessageContract(IsWrapped = false)]
public class TestResponseContract
{
[MessageBodyMember]
public TestResponse TestResponse { get; set; }
}
public class Service : ITestService
{
public TestResponseContract Test(TestRequestContract request)
{
return new TestResponseContract { TestResponse = new TestResponse { ID = request.TestRequest.ID } };
}
}

public static void Test()
{
string baseAddress = "http://" + Environment.MachineName + ":8000/Service";
ServiceHost host = new ServiceHost(typeof(Service), new Uri(baseAddress));
host.AddServiceEndpoint(typeof(ITestService), new BasicHttpBinding(), "soap");
host.AddServiceEndpoint(typeof(ITestService), new WebHttpBinding(), "rest").Behaviors.Add(new WebHttpBehavior());
host.Open();
Console.WriteLine("Host opened");

var factory = new ChannelFactory<ITestService>(new BasicHttpBinding(), new EndpointAddress(baseAddress + "/soap"));
var proxy = factory.CreateChannel();
var input = new TestRequestContract { TestRequest = new TestRequest { InnerTestRequest = new InnerTestRequest { ID = 2 }, ID = 4 } };
Console.WriteLine(proxy.Test(input).TestResponse.ID);

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

factory = new ChannelFactory<ITestService>(new WebHttpBinding(), new EndpointAddress(baseAddress + "/rest"));
factory.Endpoint.Behaviors.Add(new WebHttpBehavior());
proxy = factory.CreateChannel();
Console.WriteLine(proxy.Test(input).TestResponse.ID);

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

Console.WriteLine();
Console.WriteLine("Now using the inputs from the OP");
foreach (bool useSoap in new bool[] { true, false })
{
WebClient c = new WebClient();
c.Headers[HttpRequestHeader.ContentType] = "text/xml";
if (useSoap)
{
c.Headers["SOAPAction"] = "http://www.mysite.com/Test/ITestService/Test";
}

string uri = useSoap ?
baseAddress + "/soap" :
baseAddress + "/rest/Test";

Console.WriteLine("Request to {0}", uri);

string body = @"<TestRequest xmlns=""http://www.mysite.com/Test"">
<InnerTestRequest>
<ID>2</ID>
</InnerTestRequest>
<ID>4</ID>
</TestRequest>";
if (useSoap)
{
body = "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body>" +
body +
"</s:Body></s:Envelope>";
}

Console.WriteLine(c.UploadString(uri, body));
Console.WriteLine();
}

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

关于c# - WCF DataContracts - 如何将单个 DataContract 与复杂对象同时用于 WCF SOAP 和 REST 服务?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15252991/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com