2

I have some code that generates XML output.

The header should look like this:

<SOAP-ENV:Envelope 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns="http://fedex.com/ws/rate/v31">

However, my code is outputting this:

<SOAP-ENV:Envelope 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns="http://fedex.com/ws/rate/v31" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">

For some reason, the xmlns:SOAP-ENV is at the end instead of the beginning. Not sure why.

The code:

Using Request As XmlWriter = XmlWriter.Create(RequestXMLString)
                    'Start ConfirmRequest Document
                    Request.WriteStartDocument(False)

                    Request.WriteStartElement("SOAP-ENV", "Envelope", "http://schemas.xmlsoap.org/soap/envelope/")
                    Request.WriteAttributeString("xmlns", "SOAP-ENC", Nothing, "http://schemas.xmlsoap.org/soap/encoding/")
                    Request.WriteAttributeString("xmlns", "xsi", Nothing, "http://www.w3.org/2001/XMLSchema-instance")
                    Request.WriteAttributeString("xmlns", "xsd", Nothing, "http://www.w3.org/2001/XMLSchema")
                    Request.WriteAttributeString("xmlns", "", Nothing, "http://fedex.com/ws/rate/v31")

                    Request.WriteStartElement("Body", "http://schemas.xmlsoap.org/soap/envelope/")

'...rest of code...

Full SOAP output:

<?xml version="1.0" encoding="utf-16" standalone="no"?>
<SOAP-ENV:Envelope 
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
xmlns="http://fedex.com/ws/rate/v31" 
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
   <SOAP-ENV:Body>
      <RateRequest>
         <WebAuthenticationDetail>
            <ParentCredential>
               <Key>KEY</Key>
               <Password>PASSWORD</Password>
            </ParentCredential>
            <UserCredential>
               <Key>KEY</Key>
               <Password>PASSWORD</Password>
            </UserCredential>
         </WebAuthenticationDetail>
         <ClientDetail>
            <AccountNumber>ACCTNUM</AccountNumber>
            <MeterNumber>METERNUM</MeterNumber>
            <SoftwareId>WSXI</SoftwareId>
         </ClientDetail>
         <TransactionDetail>
            <CustomerTransactionId>ID</CustomerTransactionId>
         </TransactionDetail>
         <Version>
            <ServiceId>crs</ServiceId>
            <Major>31</Major>
            <Intermediate>0</Intermediate>
            <Minor>0</Minor>
         </Version>
         <RequestedShipment>
            <ShipTimestamp>2/7/2022 10:57:05 AM</ShipTimestamp>
            <DropoffType>REGULAR_PICKUP</DropoffType>
            <ServiceType>FEDEX_GROUND</ServiceType>
            <PackagingType>FEDEX_BOX</PackagingType>
            <Shipper>
               <AccountNumber>ACCTNUM</AccountNumber>
               <Contact>
                  <CompanyName>COMPANYNAME</CompanyName>
                  <PhoneNumber>111-111-1111</PhoneNumber>
               </Contact>
               <Address>
                  <StreetLines>ADDRESS1</StreetLines>
                  <StreetLines>ADDRESS2</StreetLines>
                  <City>CITY</City>
                  <StateOrProvinceCode>ZZ</StateOrProvinceCode>
                  <PostalCode>11111</PostalCode>
                  <CountryCode>US</CountryCode>
               </Address>
            </Shipper>
            <Recipient>
               <AccountNumber>ACCTNUM</AccountNumber>
               <Contact>
                  <PersonName>DUDE</PersonName>
                  <PhoneNumber>111-111-1111</PhoneNumber>
               </Contact>
               <Address>
                  <StreetLines>ADDRESS1</StreetLines>
                  <City>CITY</City>
                  <StateOrProvinceCode>ZZ</StateOrProvinceCode>
                  <PostalCode>11111-0000</PostalCode>
                  <CountryCode>US</CountryCode>
               </Address>
            </Recipient>
            <ShippingChargesPayment>
               <PaymentType>SENDER</PaymentType>
               <Payor>
                  <ResponsibleParty>
                     <AccountNumber>ACCTNUM</AccountNumber>
                     <Tins>
                        <TinType>BUSINESS_STATE</TinType>
                        <Number>123456</Number>
                     </Tins>
                  </ResponsibleParty>
               </Payor>
            </ShippingChargesPayment>
            <RateRequestTypes>LIST</RateRequestTypes>
            <PackageCount>1</PackageCount>
            <RequestedPackageLineItems>
               <SequenceNumber>1</SequenceNumber>
               <GroupNumber>1</GroupNumber>
               <GroupPackageCount>1</GroupPackageCount>
               <Weight>
                  <Units>LB</Units>
                  <Value>1.05</Value>
               </Weight>
               <Dimensions>
                  <Length>12</Length>
                  <Width>12</Width>
                  <Height>12</Height>
                  <Units>IN</Units>
               </Dimensions>
               <ContentRecords>
                  <PartNumber>123456</PartNumber>
                  <ItemNumber>ITEMNUM</ItemNumber>
                  <ReceivedQuantity>12</ReceivedQuantity>
                  <Description>ContentDescription</Description>
               </ContentRecords>
            </RequestedPackageLineItems>
         </RequestedShipment>
      </RateRequest>
   </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

The section of code that deals with sending the request:

Dim RequestOutput As String = RequestXMLString.ToString()
Dim HttpRequest As String = RequestOutput 
Dim XMLVar As String = ""
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

Dim FedexRequest As HttpWebRequest = DirectCast(WebRequest.Create("https://ws.fedex.com:443/web-services"), HttpWebRequest)

Dim buffer As Byte() = Encoding.ASCII.GetBytes(HttpRequest)

FedexRequest.Credentials = CredentialCache.DefaultCredentials
FedexRequest.Method = "POST"
FedexRequest.ContentType = "application/x-www-form-urlencoded"
FedexRequest.ContentLength = buffer.Length

Dim receiveStream As Stream = FedexRequest.GetRequestStream()

receiveStream.Write(buffer, 0, buffer.Length)
receiveStream.Close()

Dim UPSResponse As HttpWebResponse = DirectCast(FedexRequest.GetResponse, HttpWebResponse) 'Error occurs here
Dim answerStream As Stream = UPSResponse.GetResponseStream()
Dim _answerStream As StreamReader = New StreamReader(answerStream)

XMLVar = _answerStream.ReadToEnd().ToString
ThisForm.Variables("XMLVar").Value = XMLVar
whatwhatwhat
  • 1,991
  • 4
  • 31
  • 50
  • 1
    Is this actually a problem? – Julia Feb 07 '22 at 20:10
  • 1
    The order of attributes (including `xmlns:` attributes) in an XML element is insignificant. Software that has a dependency on attribute ordering in _incorrect_. Do you _really_ need to render the attributes in a particular order? – Dai Feb 07 '22 at 20:10
  • Also, `System.Xml.*` is a very unergonomic XML library that no-one enjoys using - is there a reason you're not using the far superior `System.Xml.Linq` API instead? – Dai Feb 07 '22 at 20:11
  • @Dai well the server is returning a 400 error. This is the first issue I found when comparing my XML output to the docs on the Fedex website. – whatwhatwhat Feb 07 '22 at 20:11
  • @Dai is it easier to make an HTTP request using `System.Xml.Linq` than what I already have? – whatwhatwhat Feb 07 '22 at 20:12
  • https://www.fedex.com/us/developer/downloads/pdfs/2021/FedEx_WebServices_DevelopersGuide_v2021.pdf Page 703 – whatwhatwhat Feb 07 '22 at 20:12
  • 1
    I'd say skip all XML processing entirely and just render out a composite `string` (that contains the XML) with placeholders. What does your _full_ SOAP message look like? – Dai Feb 07 '22 at 20:12
  • 2
    Also, I hope you're invoicing your client 4x your usual rate (2x because it's SOAP, and another 2x for VB.NET). – Dai Feb 07 '22 at 20:13
  • @Dai haha good call. I added the full SOAP message. Besides the one attribute of the header being in the wrong place I don't see any other issue and yet the server thinks my request is wrong... – whatwhatwhat Feb 07 '22 at 20:18
  • Doesn't the response-body of the HTTP 400 response from FedEx include an error-description or validation error list? – Dai Feb 07 '22 at 20:40
  • @Dai good point. That would be really helpful lol. How do I print that using `System.Xml`? – whatwhatwhat Feb 07 '22 at 20:51
  • Let's ignore `System.Xml` entirely - how are you making your HTTP requests to Fedex? Are you using `HttpClient`? – Dai Feb 07 '22 at 20:53
  • @Dai I updated the question to show the section of code that sends the request. – whatwhatwhat Feb 07 '22 at 20:59
  • @Dai `RequestXMLString` is where the XML ultimately ends up. You can ignore the redundancy in writing this to `RequestOutput` and then to `HttpRequest` as this was done to concatenate with another string (not shown). – whatwhatwhat Feb 07 '22 at 21:01
  • `FedexRequest.ContentType = "application/x-www-form-urlencoded"` <-- This is incorrect. You're posting SOAP (`application/soap+xml`) ([see here](https://stackoverflow.com/questions/26465349/what-content-type-should-be-in-http-header-of-soap-1-2-message)), not a HTML `
    `. This is like what the problem is.
    – Dai Feb 07 '22 at 21:08
  • @Dai I changed that to `application/soap+xml` but it's still giving the same error... – whatwhatwhat Feb 07 '22 at 21:12
  • @Dai it's erroring out on this line: `Dim UPSResponse As HttpWebResponse = DirectCast(FedexRequest.GetResponse, HttpWebResponse)` – whatwhatwhat Feb 07 '22 at 21:16
  • 1
    @Dai unfortunately I have encountered an Xml format from a vendor in which attributes and nodes needed to be ordered in a specific order, else they couldn't read the file. In a perfect world the order shouldn't matter, but in practice sometimes it does. – djv Feb 07 '22 at 21:18
  • @whatwhatwhat Ugh... first off, _stop using `HttpWebRequest`_ (because it throws exceptions for too many reasons), and switch to `HttpClient`, and dump the actual response. Also, try your request in a tool like Postman or Linqpad. We **need** to see Fedex's full response. – Dai Feb 07 '22 at 21:21
  • @Dai I switched to `HttpClient` and `HttpResponseMessage` but there is an error during compiling saying that these types are not defined. I'm already importing System.Net at the top - is this not enough? What library am I missing? – whatwhatwhat Feb 07 '22 at 21:28
  • Or maybe I just did it too naively. This is what I changed both lines to: `Dim FedexRequest As HttpClient = DirectCast(WebRequest.Create("https://ws.fedex.com:443/web-services"), HttpClient)` `Dim UPSResponse As HttpResponseMessage = DirectCast(FedexRequest.GetResponse, HttpResponseMessage)` – whatwhatwhat Feb 07 '22 at 21:31
  • @whatwhatwhat That's not how to use `HttpClient`. See here: https://www.dotnetperls.com/httpclient-vbnet – Dai Feb 07 '22 at 21:36
  • @Dai ok I fixed it according to the link you provided. However, I feel as though I've just run into a wall because the compiler is throwing this error when I add `Imports System.Net.Http` at the top: `Namespace or type specified in the Imports 'System.Net.Http' doesn't contain any public member or cannot be found. Make sure the namespace or the type is defined and contains at least one public member. Make sure the imported element name doesn't use any aliases.` – whatwhatwhat Feb 07 '22 at 21:45
  • What version of .NET are you targeting? – Dai Feb 07 '22 at 21:46
  • @Dai not sure. I was given a smart client to be able to work on the modifications locally from my laptop. I don't have access to the client's server. – whatwhatwhat Feb 07 '22 at 21:50
  • @Dai Is there a way to get this done using `HttpWebRequest`? I know it's not ideal, but I doubt I could get the client to upgrade their .NET version. – whatwhatwhat Feb 07 '22 at 21:54
  • @whatwhatwhat welp... _rip_. Not much I can do from here. – Dai Feb 07 '22 at 21:54
  • @Dai aw no worries!! Thanks for everything!! – whatwhatwhat Feb 07 '22 at 21:59
  • "I have encountered an Xml format from a vendor in which attributes and nodes needed to be ordered in a specific order, else they couldn't read the file." Fix the bug. If you've got a plug that won't fit in a socket because the socket is the wrong size, fix the socket, not the plug. – Michael Kay Feb 07 '22 at 22:40

0 Answers0