C#WCF全局命名空间-皇家邮件 [英] C# WCF Global Namespaces - Royal Mail
问题描述
我有一个正在生成请求的WCF SOAP客户端。服务器将其拒绝为无效请求。我已经使用SOAPUI将其追溯到名称空间,但无法弄清楚如何使客户端产生所需的结果。
I've got a WCF SOAP client which is generating a request. This is being refused by the server as an invalid request. I've traced it down to namespaces using SOAPUI but cannot figure out how I can get the client to produce the required result.
客户端是作为Web服务参考生成的从wsdl发送并产生以下SOAP消息;
The client was generated as a web service reference from the wsdl and is producing the following SOAP message;
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header></s:Header>
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<createShipmentRequest xmlns="http://www.royalmailgroup.com/api/ship/V2">
<integrationHeader>
<dateTime xmlns="http://www.royalmailgroup.com/integration/core/V1">2015-07-23</dateTime>
<version xmlns="http://www.royalmailgroup.com/integration/core/V1">2</version>
<identification xmlns="http://www.royalmailgroup.com/integration/core/V1">
<applicationId>some random number</applicationId>
<transactionId>some reference number</transactionId>
</identification>
</integrationHeader>
</createShipmentRequest>
</s:Body>
</s:Envelope>
如您所见,名称空间正在各个元素上输出...
As you can see the namespaces are being outputted on the individual elements...
我的工作示例在SOAP信封中定义了名称空间;
The working example I have has the namespaces defined in the SOAP Envelope;
<s:Envelope xmlns:v2="http://www.royalmailgroup.com/api/ship/V2" xmlns:v1="http://www.royalmailgroup.com/integration/core/V1" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<s:Header></s:Header>
<s:Body>
<v2:createShipmentRequest>
<v2:integrationHeader>
<v1:dateTime>2015-07-23</v1:dateTime>
<v1:version>2</v1:version>
<v1:identification>
<v1:applicationId>some random number</v1:applicationId>
<v1:transactionId>some reference number</v1:transactionId>
</v1:identification>
</v2:integrationHeader>
</v2:createShipmentRequest>
</s:Body>
</s:Envelope>
根据我的理解,这没有什么区别,但是服务器只是拒绝了请求。修改了我在SOAPUI中生成的请求后,这无疑导致了问题,因此如何移动两个名称空间定义v1和? v2到SOAP信封,然后让正确的元素使用正确的前缀?
From what I understand this should not make a difference but the sever simply rejects the request. After modifying my generated request in SOAPUI it is defiantly this causing the problem, so how can I move the two namespace definitions v1 & v2 to the SOAP Envelope and then have the correct elements use the correct prefix?
我的客户端使用以下函数启动;
My client is initiated using the following function;
private shippingAPIPortTypeClient GetProxy() {
BasicHttpBinding myBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
shippingClient = new shippingAPIPortTypeClient(myBinding, new EndpointAddress(new Uri(shippingClientSandboxEndpoint), EndpointIdentity.CreateDnsIdentity("api.royalmail.com"), new AddressHeaderCollection()));
shippingClient.ClientCredentials.ClientCertificate.Certificate = certificate;
return shippingClient;
}
推荐答案
所以我需要创建自定义MessageFormatter并将其作为行为附加到客户端操作。
So it turns out I needed to create a custom MessageFormatter and attach it as a behaviour to the client operations.
对于需要执行此操作的其他任何人,您需要3个文件;
For anybody else needing to do this you need 3 files;
首先,创建一个实现Message的自定义消息类。在这里,您可以在OnWriteStartEnvelope方法中添加/定义所需的名称空间。
Firstly you create a custom message class which implements Message. Here in the OnWriteStartEnvelope method you add/define the namespaces you want in the Envelope.
class RoyalMailMessage: Message {
private readonly Message message;
public RoyalMailMessage(Message message) {
this.message = message;
}
public override MessageHeaders Headers {
get {
return this.message.Headers;
}
}
public override MessageProperties Properties {
get {
return this.message.Properties;
}
}
public override MessageVersion Version {
get {
return this.message.Version;
}
}
protected override void OnWriteStartBody(XmlDictionaryWriter writer) {
writer.WriteStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/");
}
protected override void OnWriteBodyContents(XmlDictionaryWriter writer) {
this.message.WriteBodyContents(writer);
}
protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer) {
writer.WriteStartElement("s", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/");
writer.WriteAttributeString("xmlns", "v2", null, "http://www.royalmailgroup.com/api/ship/V2");
writer.WriteAttributeString("xmlns", "v1", null, "http://www.royalmailgroup.com/integration/core/V1");
writer.WriteAttributeString("xmlns", "xsi", null, "http://www.w3.org/2001/XMLSchema-instance");
writer.WriteAttributeString("xmlns", "xsd", null, "http://www.w3.org/2001/XMLSchema");
}
}
然后创建一个自定义类实现IClientMessageFormatter。这利用了我们上面定义的Message类来处理客户端发出的请求;
Then you create a custom Class which implements IClientMessageFormatter. This makes use of the Message class we defined above for outgoing requests made by the client;
public class RoyalMailMessageFormatter: IClientMessageFormatter {
private readonly IClientMessageFormatter formatter;
public RoyalMailMessageFormatter(IClientMessageFormatter formatter) {
this.formatter = formatter;
}
public object DeserializeReply(Message message, object[] parameters) {
return this.formatter.DeserializeReply(message, parameters);
}
public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) {
var message = this.formatter.SerializeRequest(messageVersion, parameters);
return new RoyalMailMessage(message);
}
}
然后我们需要创建一个实现IOperationBehavior的自定义类。这是必需的,因此我们可以将自定义消息格式化程序作为一种行为添加到服务操作中;
We then need to create a custom class which implements IOperationBehavior. This is needed so we can attatch the custom message formatter to the service operations as a behaviour;
class RoyalMailIEndpointBehavior: IOperationBehavior {
public RoyalMailIEndpointBehavior() {}
public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy) {
IClientMessageFormatter currentFormatter = proxy.Formatter;
proxy.Formatter = new RoyalMailMessageFormatter(currentFormatter);
}
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) {
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) {
}
public void Validate(OperationDescription operationDescription) {
}
}
最后,我们需要向WCF生成的所有服务操作中添加自定义IOperation行为;
Finally, we need to add the custom IOperation behaviour to all the service operations generated by WCF;
private shippingAPIPortTypeClient GetProxy() {
BasicHttpBinding myBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport);
myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
shippingClient = new shippingAPIPortTypeClient(myBinding, new EndpointAddress(new Uri(shippingClientSandboxEndpoint), EndpointIdentity.CreateDnsIdentity("api.royalmail.com"), new AddressHeaderCollection()));
shippingClient.ClientCredentials.ClientCertificate.Certificate = certificate;
foreach(OperationDescription od in shippingClient.Endpoint.Contract.Operations) {
od.Behaviors.Add(new RoyalMailIEndpointBehavior());
}
return shippingClient;
}
名称空间现在应该在SOAP信封中,并且所有元素都使用正确的前缀给我们类似的东西;
The namespaces should now be in the SOAP Envelope and the elements all use the correct prefix giving us something like;
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v2="http://www.royalmailgroup.com/api/ship/V2" xmlns:v1="http://www.royalmailgroup.com/integration/core/V1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<s:Header></s:Header>
<s:Body>
<v2:createShipmentRequest>
<v2:integrationHeader>
<v1:dateTime>2015-07-23T20:37:07.937+01:00</v1:dateTime>
<v1:version>2</v1:version>
<v1:identification>
<v1:applicationId>SOME RANDOM ID</v1:applicationId>
<v1:transactionId>SOME RANDOM ID</v1:transactionId>
</v1:identification>
</v2:integrationHeader>
</v2:createShipmentRequest>
</s:Body>
</s:Envelope>
这篇关于C#WCF全局命名空间-皇家邮件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!