2015-07-23 15 views
5

Mam klienta SOF WCF, który generuje żądanie. Serwer odmawia tego jako nieprawidłowego żądania. Wykreśliłem to do przestrzeni nazw przy użyciu SOAPUI, ale nie mogę dowiedzieć się, w jaki sposób mogę uzyskać klienta, aby uzyskać wymagany wynik.C# WCF Global Namespaces - Royal Mail

Klient został wygenerowany jako odnośnik do usługi WWW z pliku wsdl i tworzy następujący komunikat SOAP;

<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> 

Jak widać przestrzenie nazw są wyprowadzane na poszczególnych elementach ...

Przykład pracy mam ma nazw zdefiniowanych w kopercie SOAP;

<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> 

Z tego co rozumiem, nie powinno to mieć znaczenia, ale operator po prostu odrzuca prośbę. Po zmodyfikowaniu wygenerowanego żądania w SOAPUI jest to wyzywająco powodujące problem, więc jak mogę przenieść dwie definicje przestrzeni nazw v1 & v2 do Envelope SOAP, a następnie mieć prawidłowe elementy używać prawidłowego przedrostka?

Mój klient jest inicjowany za pomocą następującej funkcji;

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; 
} 
+1

Właśnie natknąłem się na DOKŁADNIE ten sam irytujący problem z Royal Mail. Czasy marnowania czasu na ich usługi po prostu nie spełniają standardów XML. Dziękujemy za przesłanie rozwiązania - wypróbuję to. – Vok

+0

skąd otrzymałeś shippingAPIPortTypeClient? –

+0

@ ReiA.Banaag Klient jest generowany jako odniesienie do usługi internetowej za pomocą wizualnego studio. Zasadniczo tworzysz nową usługę WWW i przeglądasz katalog, w którym masz plik WSDL Royal Mail API. Visual Studio stworzy wtedy wszystkie klasy i klienta. Zobacz https://msdn.microsoft.com/en-us/library/bb628649.aspx dla szybkiego przewodnika – Ashley

Odpowiedz

5

Okazuje się, że potrzebowałem stworzyć niestandardowy MessageFormatter i dołączyć go jako zachowanie do operacji klienta.

Dla każdego, kto tego potrzebuje, potrzebujesz 3 plików;

Najpierw tworzymy niestandardową klasę wiadomości, która implementuje wiadomość. Tutaj w metodzie OnWriteStartEnvelope dodajesz/definiujesz przestrzeń nazw w Kopercie.

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"); 

    } 
} 

Następnie tworzysz niestandardową klasę, która implementuje IClientMessageFormatter. Wykorzystuje to klasę wiadomości, którą zdefiniowaliśmy powyżej dla wysyłanych żądań klienta;

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); 
    } 
} 

Następnie musimy stworzyć niestandardową klasę, która implementuje IOperationBehavior. Jest to potrzebne, abyśmy mogli sprawdzić niestandardowy formater wiadomości w działaniach serwisowych jako zachowanie;

Wreszcie, musimy dodać niestandardowe zachowanie IOperation do wszystkich operacji serwisowych generowanych przez 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; 
} 

Przestrzenie nazw powinny znajdować się w kopercie SOAP, a wszystkie elementy powinny zawierać poprawny prefiks, dając nam coś takiego;

<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> 
+2

Ty, mój przyjaciel, właśnie uratowałeś mi godziny pracy i frustrację. – nfplee

+0

Postępowałem zgodnie z tym samym ale nadal otrzymuję błąd nieprawidłowego żądania. Czy pomożesz mi rozwiązać ten problem? –

+0

@AmmarKhan To prawdopodobnie najlepiej, jeśli otworzysz nowe pytanie z powodu otrzymywanego błędu, kodu, który masz, oczekiwanych i rzeczywistych rezultatów itd. Użyj charles, fiddler lub wireshark, aby uchwycić Twoją prośbę i odpowiedź. – Ashley