2016-01-27 24 views
1

我正在嘗試將皇家郵件SOAP API集成到我的.NET代碼中。我已經按照這裏的建議Consume WCF Royal Mail API in c# Console Application和這裏C# WCF Namespaces Move To Header & Use NS Prefix皇家郵件運輸API C#

我已經創建了一個自定義IClientMessageFormatter,以便能夠將名稱空間附加到soap信封的開頭,但我似乎仍無法使其工作。我不斷收到以下錯誤。無法爲權限爲'api.royalmail.com'的SSL/TLS安全通道建立信任關係,內部例外是:根據驗證過程,遠程證書無效。

我正在使用Visual Studio 13和.Net 3.5版,我嘗試了很多其他版本,但沒有進一步的進展。當我調試我可以看到正常的消息已傳遞到RoyalMailMessage但運行時OnWriteStartEnvelope我看不到_message對象的任何更改。我創建了一個跟蹤來查看肥皂請求已發送。

我已經將我的XML請求發送給皇家郵件支持人員,他們驗證它失敗的原因是由於未在信封中聲明名稱空間以及缺少前綴。

RoyalMail.cs

internal class RoyalMail 
{ 
    private readonly X509Certificate2 _certificate; 
    private readonly Config _config; 

    public RoyalMail() 
    { 
     _config = new Config(); 
     _config.LoadConfig(); 

     // Load The SSL Certificate (Check The File Exists) 
     var certificatePath = (Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) + @"\" + _config.GetCertificateName()); 

     if (!File.Exists(certificatePath)) 
     { 
      throw new Exception(@"The Royal Mail Certificate Is Missing From The Plugins Directory. Please Place The File " + _config.GetCertificateName() + " In The Same Directory As The Plugin DLL File & Relaunch FileMaker.\n\n" + certificatePath); 
     } 

     _certificate = new X509Certificate2(certificatePath, _config.GetCertificatePassword()); 

     // Check It's In The Certificate 
     var store = new X509Store(StoreName.My, StoreLocation.CurrentUser); 
     store.Open(OpenFlags.ReadWrite); 
     if (!store.Certificates.Contains(_certificate)) 
     { 
      store.Add(_certificate); 
      MessageBox.Show("Certificate Was Installed Into Computer Trust Store"); 
     } 
     store.Close(); 
    } 


    /* 
    * 
    * SOAP Service & Methods 
    * 
    */ 

    private shippingAPIPortTypeClient GetProxy() 
    { 

     var myBinding = new BasicHttpBinding(BasicHttpSecurityMode.Transport) 
     { 
      MaxReceivedMessageSize = 2147483647 
     }; 
     myBinding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate; 
     var uri = new Uri(_config.GetEndpointUrl()); 
     var endpointIdentity = EndpointIdentity.CreateDnsIdentity("api.royalmail.com"); 

     var shippingClient = new shippingAPIPortTypeClient(myBinding, new EndpointAddress(uri, endpointIdentity, new AddressHeaderCollection())); 
     if (shippingClient.ClientCredentials != null) 
      shippingClient.ClientCredentials.ClientCertificate.Certificate = _certificate; 

     foreach (var od in shippingClient.Endpoint.Contract.Operations) 
     { 
      od.Behaviors.Add(new RoyalMailIEndpointBehavior()); 
     } 

     return shippingClient; 
    } 

    private SecurityHeaderType GetSecurityHeaderType() 
    { 
     var securityHeader = new SecurityHeaderType(); 

     var creationDate = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); 

     var nonce = (new Random().Next(0, int.MaxValue)).ToString(); 

     var hashedPassword = GetSha1(_config.GetPassword()); 

     var concatednatedDigestInput = string.Concat(nonce, creationDate, Encoding.Default.GetString(hashedPassword)); 
     var digest = GetSha1(concatednatedDigestInput); 

     var passwordDigest = Convert.ToBase64String(digest); 

     var encodedNonce = Convert.ToBase64String(Encoding.Default.GetBytes(nonce)); 

     var doc = new XmlDocument(); 
     using (var writer = doc.CreateNavigator().AppendChild()) 
     { 
      writer.WriteStartDocument(); 
      writer.WriteStartElement("wsse", "Security", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); 
      writer.WriteStartElement("wsse", "UsernameToken", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"); 
      writer.WriteElementString("wsse", "Username", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", _config.GetUsername()); 
      writer.WriteElementString("wsse", "Password", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", passwordDigest); 
      writer.WriteElementString("wsse", "Nonce", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", encodedNonce); 
      writer.WriteElementString("wsse", "Created", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", creationDate); 
      writer.WriteEndElement(); 
      writer.WriteEndElement(); 
      writer.WriteEndDocument(); 
      writer.Flush(); 
     } 

     if (doc.DocumentElement != null) 
     { 
      doc.DocumentElement.RemoveAllAttributes(); 

      var headers = doc.DocumentElement.ChildNodes.Cast<XmlElement>().ToArray(); 

      securityHeader.Any = headers; 
     } 

     return securityHeader; 
    } 

    private integrationHeader GetIntegrationHeader() 
    { 

     var header = new integrationHeader(); 

     var created = DateTime.Now; 
     var createdAt = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ"); 

     header.dateTime = created; 
     header.version = int.Parse(_config.GetVersion()); 
     header.dateTimeSpecified = true; 
     header.versionSpecified = true; 

     var idStructure = new identificationStructure {applicationId = _config.GetApplicationId()}; 

     var nonce = new Random().Next(0, int.MaxValue).ToString(); 

     idStructure.transactionId = CalculateMd5Hash(nonce + createdAt); 

     header.identification = idStructure; 

     return header; 
    } 

    private static byte[] GetSha1(string input) 
    { 
     return SHA1Managed.Create().ComputeHash(Encoding.Default.GetBytes(input)); 
    } 

    public string CalculateMd5Hash(string input) 
    { 
     // step 1, calculate MD5 hash from input 
     var md5 = MD5.Create(); 
     var inputBytes = Encoding.ASCII.GetBytes(input); 
     var hash = md5.ComputeHash(inputBytes); 

     // step 2, convert byte array to hex string 
     var sb = new StringBuilder(); 
     foreach (var t in hash) 
     { 
      sb.Append(t.ToString("X2")); 
     } 
     return sb.ToString(); 
    } 

    /* 
    * Check Response Footer For Errors & Warnings From Service 
    * If Error Return True So We Can Inform File maker Of Error 
    * Ignore Warnings For Now 
    * 
    */ 
    private static void CheckErrorsAndWarnings(integrationFooter integrationFooter) 
    { 
     if (integrationFooter != null) 
     { 
      if (integrationFooter.errors != null && integrationFooter.errors.Length > 0) 
      { 
       var errors = integrationFooter.errors; 
       foreach (var error in errors) 
       { 
        MessageBox.Show("Royal Mail Request Error: " + error.errorDescription + ". " + error.errorResolution, "Royal Mail Request Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); 
       } 
       if (errors.Length > 0) 
       { 
        return; 
       } 
      } 

      if (integrationFooter.warnings != null && integrationFooter.warnings.Length > 0) 
      { 
       var warnings = integrationFooter.warnings; 
       foreach (var warning in warnings) 
       { 
        MessageBox.Show("Royal Mail Request Warning: " + warning.warningDescription + ". " + warning.warningResolution, "Royal Mail Request Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning, MessageBoxDefaultButton.Button1); 
       } 
      } 
     } 
    } 

    /* 
    * Show Message Box With SOAP Error If We Receive A Fault Code Back From Service 
    * 
    */ 
    private static void ShowSoapException(FaultException e) 
    { 
     var message = e.CreateMessageFault(); 
     var errorDetail = message.GetDetail<XmlElement>(); 
     var errorDetails = errorDetail.ChildNodes; 
     var fullErrorDetails = ""; 

     for (var i = 0; i < errorDetails.Count; i++) 
     { 
      var xmlNode = errorDetails.Item(i); 
      if (xmlNode != null) 
       fullErrorDetails += xmlNode.Name + ": " + xmlNode.InnerText + "\n"; 
     } 

     MessageBox.Show("An Error Occured With Royal Mail Service: " + message.Reason + "\n\n" + fullErrorDetails, "Royal Mail SOAP Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); 
    } 

    public createShipmentResponse SendCreateShipmentRequest(CreateShipmentForm shippingForm) 
    { 
     var client = GetProxy(); 

     try 
     { 
      var request = new createShipmentRequest {integrationHeader = GetIntegrationHeader()}; 
      var shipment = new requestedShipment(); 

      // Shipment Type Code (Delivery or Return) 
      var shipmentType = new referenceDataType {code = shippingForm.ShippingType}; 
      shipment.shipmentType = shipmentType; 

      // Service Type Code (1:24H 1st Class, 2: 48H 2nd Class, D: Special Delivery Guaranteed, H: HM Forces (BFPO), I: International, R: Tracked Returns, T: Tracked Domestic) 
      var serviceType = new referenceDataType {code = shippingForm.ServiceType}; 
      shipment.serviceType = serviceType; 

      // Service Offering (See Royal Mail Service Offering Type Codes. Too Many To List) 
      var serviceOfferingTypeContainer = new serviceOfferingType(); 
      var serviceOffering = new referenceDataType {code = shippingForm.ServiceOffering}; 
      serviceOfferingTypeContainer.serviceOfferingCode = serviceOffering; 
      shipment.serviceOffering = serviceOfferingTypeContainer; 

      // Service Format Code 
      var serviceFormatTypeContainer = new serviceFormatType(); 
      var serviceFormat = new referenceDataType {code = shippingForm.ServiceFormat}; 
      serviceFormatTypeContainer.serviceFormatCode = serviceFormat; 
      shipment.serviceFormat = serviceFormatTypeContainer; 

      // Shipping Date 
      shipment.shippingDate = shippingForm.ShippingDate; 
      shipment.shippingDateSpecified = true; 

      shipment.signature = true; 

      shipment.signatureSpecified = true; 

      // Sender Reference Number (e.g. Invoice Number or RA Number) 
      shipment.senderReference = shippingForm.InvoiceNumber; 

      /* 
      * Service Enhancements 
      */ 

      var serviceEnhancements = new List<serviceEnhancementType>(); 
      shipment.serviceEnhancements = serviceEnhancements.ToArray(); 


      /* 
      * Recipient Contact Details 
      */ 

      var recipientContact = new contact(); 
      recipientContact.complementaryName = shippingForm.Company; 
      recipientContact.name = shippingForm.Name; 

      if(!shippingForm.EmailAddress.Equals("")) { 
       var email = new digitalAddress {electronicAddress = shippingForm.EmailAddress}; 
       recipientContact.electronicAddress = email; 
      } 

      if(!shippingForm.MobileNumber.Equals("")) { 
       var tel = new telephoneNumber(); 
       var phoneRegex = new Regex(@"[^\d]"); 
       tel.telephoneNumber1 = phoneRegex.Replace(shippingForm.MobileNumber, ""); 
       tel.countryCode = "00" + shippingForm.CountryDiallingCode; 
       recipientContact.telephoneNumber = tel; 
      } 

      shipment.recipientContact = recipientContact; 

      /* 
      * Recipient Address 
      * 
      */ 
      var recipientAddress = new address 
      { 
       addressLine1 = shippingForm.AddressLine1, 
       addressLine2 = shippingForm.AddressLine2, 
       addressLine3 = shippingForm.AddressLine3, 
       addressLine4 = shippingForm.County, 
       postTown = shippingForm.Town 
      }; 
      var country = new countryType(); 
      var countryCode = new referenceDataType { code = shippingForm.CountryCode }; 
      country.countryCode = countryCode; 
      recipientAddress.country = country; 
      recipientAddress.postcode = shippingForm.PostCode; 

      recipientAddress.stateOrProvince = new stateOrProvinceType {stateOrProvinceCode = new referenceDataType()}; 

      shipment.recipientAddress = recipientAddress; 

      // Shipment Items 

      var items = new List<item>(); 

      foreach(var i in shippingForm.Items) { 
       var item = new item 
       { 
        numberOfItems = i.Products.Count.ToString(), 
        weight = new dimension 
        { 
         value = i.Weight*1000, 
         unitOfMeasure = new unitOfMeasureType {unitOfMeasureCode = new referenceDataType {code = "g"}} 
        } 
       }; 

       items.Add(item); 
      } 

      if (shippingForm.ServiceType.Contains("international")) 
      { 
       var internationalInfo = new internationalInfo 
       { 
        shipperExporterVatNo = _config.GetVatNumber(), 
        documentsOnly = false, 
        shipmentDescription = "Invoice Number: " + shippingForm.InvoiceNumber, 
        invoiceDate = DateTime.Now, 
        termsOfDelivery = "EXW", 
        invoiceDateSpecified = true, 
        purchaseOrderRef = shippingForm.InvoiceNumber 
       }; 

       var parcels = new List<parcel>(); 
       foreach (var i in shippingForm.Items) 
       { 
        var parcel = new parcel 
        { 
         weight = new dimension 
         { 
          value = i.Weight*1000, 
          unitOfMeasure = new unitOfMeasureType 
          { 
           unitOfMeasureCode = new referenceDataType {code = "g"} 
          } 
         }, 
         invoiceNumber = shippingForm.InvoiceNumber, 
         purposeOfShipment = new referenceDataType {code = "31"} 
        }; 


        var contents = new List<contentDetail>(); 
        foreach (var product in i.Products) 
        { 
         var contentDetail = new contentDetail 
         { 
          articleReference = product.Sku, 
          countryOfManufacture = new countryType 
          { 
           countryCode = new referenceDataType 
           { 
            code = product.CountryOfManufacture 
           } 
          }, 
          currencyCode = new referenceDataType {code = product.CurrencyCode}, 
          description = product.Name, 
          unitQuantity = product.Qty.ToString(), 
          unitValue = product.Price, 
          unitWeight = new dimension 
          { 
           value = Convert.ToSingle(product.Weight*1000), 
           unitOfMeasure = new unitOfMeasureType 
           { 
            unitOfMeasureCode = new referenceDataType {code = "g"} 
           } 
          } 
         }; 

         contents.Add(contentDetail); 
        } 

        //Parcel.contentDetails = Contents.ToArray(); 

        parcels.Add(parcel); 
       } 

       internationalInfo.parcels = parcels.ToArray(); 

       shipment.internationalInfo = internationalInfo; 
      } 
      else 
      { 
       shipment.items = items.ToArray(); 
      } 

      request.requestedShipment = shipment; 

      var response = client.createShipment(GetSecurityHeaderType(), request); 

      // Show Errors And Warnings 
      CheckErrorsAndWarnings(response.integrationFooter); 

      return response; 

     } 
     catch (TimeoutException e) 
     { 
      client.Abort(); 
      MessageBox.Show("Request Timed Out: " + e.Message, "Request Timeout", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); 
     } 
     catch (FaultException e) 
     { 
      client.Abort(); 
      ShowSoapException(e); 
     } 
     catch (CommunicationException e) 
     { 
      client.Abort(); 
      MessageBox.Show("A communication error has occurred: " + e.Message + " - " + e.StackTrace, "Communication Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); 
     } 
     catch (Exception e) 
     { 
      client.Abort(); 
      MessageBox.Show(e.Message, "Royal Mail Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1); 
     } 

     return null; 
    } 
} 

RoyalmailMessage.cs

class RoyalMailMessage : Message 
{ 
    public Message _message; 

    public RoyalMailMessage(Message message) 
    { 
     _message = message; 
    } 

    public override MessageHeaders Headers 
    { 
     get 
     { 
      return _message.Headers; 
     } 
    } 
    public override MessageProperties Properties 
    { 
     get 
     { 
      return _message.Properties; 
     } 
    } 

    public override MessageVersion Version 
    { 
     get 
     { 
      return _message.Version; 
     } 
    } 

    protected override void OnWriteStartBody(XmlDictionaryWriter writer) 
    { 
     writer.WriteStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/"); 
    } 

    protected override void OnWriteBodyContents(XmlDictionaryWriter writer) 
    { 
     _message.WriteBodyContents(writer); 
    } 

    protected override void OnWriteStartEnvelope(XmlDictionaryWriter writer) 
    { 
     writer.WriteStartElement("soapenv", "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"); 
    } 
} 

RoyalMailMessageFormatter.cs

public class RoyalMailMessageFormatter : IClientMessageFormatter 
{ 
    private readonly IClientMessageFormatter _formatter; 

    public RoyalMailMessageFormatter(IClientMessageFormatter formatter) 
    { 
     _formatter = formatter; 
    } 

    public object DeserializeReply(Message message, object[] parameters) 
    { 
     return _formatter.DeserializeReply(message, parameters); 
    } 

    public Message SerializeRequest(MessageVersion messageVersion, object[] parameters) 
    { 
     var message = _formatter.SerializeRequest(messageVersion, parameters); 
     return new RoyalMailMessage(message); 
    } 
} 

RoyalMailIEndpointBehavior.cs

internal class RoyalMailIEndpointBehavior : IOperationBehavior 
{ 
    public void ApplyClientBehavior(OperationDescription description, ClientOperation proxy) 
    { 
     proxy.Formatter = new RoyalMailMessageFormatter(proxy.Formatter); 
    } 

    public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters) 
    { 

    } 

    public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation) 
    { 

    } 

    public void Validate(OperationDescription operationDescription) 
    { 

    } 

} 

回答

0

有一個新的英國皇家郵政運輸API可用2,我已經失去了許多之後嘗試時間開發具有英國皇家郵政的整合,我終於找到了一種方法。我在git中分享我的項目。 https://github.com/americoa/RoyalMailShippingAPIV2