0

I am using Delphi XE5 and I am trying to send Items to Walmart using the Bulk API Upload but I am struggling with making proper connection with the server of Walmart. I have already checked some similar problems here like Walmart API - payload mime type issue

The strangest thing of all is that the feed is actually accepted by Walmart (I see it in the list of feeds) but I receive Internal server error and then meaningless characters (probably type of encoding or just kind of error) like

(#$D#$A#$1F#$008B#8#0#0#0#0#0#0#3#$D'A±'#$D#$0080'0'#$C#4'AU~'#2'v ?d'#$0087#4'?'#1#$0091'OE'#$E#$008A'O'#$1E'®?O?'#$18#$0091#$A#$0091'M^'#$C'?;'#$1D'G'#$A'dR±U?oip?m'#$1A'AVS'#4#$16'R?iR'#$1B#$0095'RO?}u'#0'enIjM'#0#0#0)

So I use Indy IdHTTP and I have tried using TIdMultipartFormDataStream and also creating the payload on my own both with receiving the internal server error. I presume I am breaking their parser somehow because if I miss one CR LF (#13#10) at the beginning I get a specific error

<?xml version="1.0" encoding="UTF-8" standalone="yes"?><ns2:errors xmlns:ns2="http://walmart.com/"><ns2:error><ns2:code>SYSTEM_ERROR.GMP_GATEWAY_API</ns2:code><ns2:field>PDR-0012</ns2:field><ns2:description></ns2:description><ns2:info>System encountered some internal error.</ns2:info><ns2:severity>ERROR</ns2:severity><ns2:category>SYSTEM</ns2:category><ns2:causes/><ns2:errorIdentifiers/></ns2:error></ns2:errors>

If I add an extra CRLF at the end then I get another error stating that there is more than one feed in the request. So I really presume that there is something which makes that server / parser return the Internal error but I don't get what and again I repeat that the feed actually gets accepted and then processed by the server in the case I receive this error.

Here is an example request with a payload created on my own:

Headers

POST /v3/feeds?feedType=item HTTP/1.1
Content-Type: multipart/form-data; boundary=qwerty
Content-Length: 5077
WM_SVC.NAME: Walmart Marketplace
WM_QOS.CORRELATION_ID: {67F0E2F9-5EAC-4E8C-9C90-650D8F7B3B7A}
WM_SEC.TIMESTAMP: 1528364885909
WM_SEC.AUTH_SIGNATURE: nSHgqzPOtzSR4wJU+U/vQJk+rk6Ke2QwodTHjzkjau2BonXZxiU9e+3NFPzaat2OUyc+vr0jqRk0H0QWTSC21PrI87mvqei5UJCJwNiIx0zVjAGpxsnIuvtIKkQsBpuUAa8C6SjTiTpDRsNt4IOxrk+tLWxlwQubWVCV+009a6o=
WM_CONSUMER.ID: 16248274-1f53-4e6d-880e-b5417698b878
WM_CONSUMER.CHANNEL.TYPE: 0f3e4dd4-0514-4346-b39d-af0e00ea066d
Host: marketplace.walmartapis.com
Accept: application/xml, */*
Accept-Encoding: identity
Accept-Language: en-US
User-Agent: Mozilla/3.0 (compatible; Indy Library)

Request

--qwerty

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<MPItemFeed xmlns:ns2="http://walmart.com/">
    <MPItemFeedHeader>
        <version>3.1</version>
        <requestId>{694A5564-7E8E-46C7-B772-C944A8C9CF99}</requestId>
        <mart>WALMART_US</mart>
    </MPItemFeedHeader>
    <MPItem>
        <processMode>REPLACE_ALL</processMode>
        <sku>w-cc-a1-4pk-white-l</sku>
        <productIdentifiers>
            <productIdentifier>
                <productIdType>UPC</productIdType>
                <productId>192082276845</productId>
            </productIdentifier>
        </productIdentifiers>
        <MPProduct>
            <SkuUpdate>Yes</SkuUpdate>
            <productName>Caramel Cantina 4 Pack Sleeping Nursing Cross Front Maternity Bra (Large, White)</productName>
            <ProductIdUpdate>Yes</ProductIdUpdate>
            <category>
                <ClothingCategory>
                    <Clothing>
                        <shortDescription>Stretchy comfort cross front sleep bra by Caramel Cantina. Whether for nursing / maternity or for wearing as a sleep bra (or both nursing and sleep bras) - you&apos;ll experience a wire free regular back strap sleep bra. This mid-weight fabric but light support wireless bra is great for all day wear and comfort.  The straps are wider than a standard bra - offering a longer range of wear time.   Great for wearing indoors (ie when you&apos;re kicking around the home or going to bed). These are padless - most moms use their own favorite nursing pads with these (though no pads are included). The front cups have two layers of fabric to add a bit more support (though we still call them our light support comfort bra). The criss cross front style allows for easy access while nursing your baby. 92% Nylon 8% Spandex fabric washes up nicely.  Cool wash and cool dry. Hang dry for longer life. The straps narrowest point at the shoulders are 1.25-1.5 inches (depending on the size). The bras are super stretchy - if you&apos;re looking for something super supportive (ie - they pull you in really tight - more like a sports bra) these probably aren&apos;t what you&apos;re looking for.  Not recommended for heavy sports activity. Best worn for activities such as walking, sleeping, lounging, light activity, running errands, etc.</shortDescription>
                        <keyFeatures>
                            <keyFeaturesValue>4 Pack Includes 4 Bras of the Same Design - Color Option Above</keyFeaturesValue>
                            <keyFeaturesValue>Soft Cup - Front Double Layered - No padding - Wireless</keyFeaturesValue>
                            <keyFeaturesValue>Super Soft Sleep In Style Bra - Great for Nursing or Sleeping</keyFeaturesValue>
                            <keyFeaturesValue>Criss Cross Front Design Gives Easy Nursing Access</keyFeaturesValue>
                            <keyFeaturesValue>Regular Straps - Scoop Back Design - Strap Width 1.25-1.5 Inches Depending on Size</keyFeaturesValue>
                        </keyFeatures>
                        <brand>Caramel Cantina</brand>
                        <mainImageUrl>https://www.dancecheerskate.com/_inv/D54173DB8F934DB2A4C68D24FAC087F5/wm-cc-a1-4pk-white.jpg</mainImageUrl>
                        <productSecondaryImageURL>
                            <productSecondaryImageURLValue>https://www.dancecheerskate.com/_inv/D54173DB8F934DB2A4C68D24FAC087F5/wm-cc-a1-wht-m1.jpg</productSecondaryImageURLValue>
                        </productSecondaryImageURL>
                        <color>White</color>
                        <clothingSize>Large</clothingSize>
                        <variantGroupId>cc-a1-4pk-parent</variantGroupId>
                        <variantAttributeNames>
                            <variantAttributeName>color</variantAttributeName>
                            <variantAttributeName>clothingSize</variantAttributeName>
                        </variantAttributeNames>
                        <countryOfOriginTextiles>Imported</countryOfOriginTextiles>
                        <fabricCareInstructions>
                            <fabricCareInstruction>Machine wash cold gentle cycle with like colors. Hang Dry. Do Not Bleach. Do Not Iron.</fabricCareInstruction>
                        </fabricCareInstructions>
                        <multipackQuantity>1</multipackQuantity>
                        <countPerPack>4</countPerPack>
                        <count>4</count>
                        <pattern>Solid</pattern>
                        <material>Nylon; Spandex</material>
                        <gender>Female</gender>
                        <ageGroup>
                            <ageGroupValue>Adult</ageGroupValue>
                        </ageGroup>
                        <clothingSizeGroup>Maternity</clothingSizeGroup>
                        <isSet>No</isSet>
                        <requiresTextileActLabeling>Yes</requiresTextileActLabeling>
                        <clothingTopStyle>Sleep Bra</clothingTopStyle>
                        <upperBodyStrapConfiguration>Regular</upperBodyStrapConfiguration>
                        <braStyle>Nursing</braStyle>
                        <clothingStyle>Maternity</clothingStyle>
                        <clothingFit>Maternity</clothingFit>
                        <isMaternity>Yes</isMaternity>
                        <swatchImages>
                            <swatchImage>
                                <swatchVariantAttribute>color</swatchVariantAttribute>
                                <swatchImageUrl>https://www.dancecheerskate.com/_inv/D54173DB8F934DB2A4C68D24FAC087F5/wm-cc-a1-4pk-white.jpg</swatchImageUrl>
                            </swatchImage>
                        </swatchImages>
                    </Clothing>
                </ClothingCategory>
            </category>
        </MPProduct>
        <MPOffer>
            <price>25.00</price>
            <ShippingWeight>
                <measure>11.80</measure>
                <unit>lb</unit>
            </ShippingWeight>
            <ProductTaxCode>2038895</ProductTaxCode>
        </MPOffer>
    </MPItem>
</MPItemFeed>
--qwerty--

The response which I get is as following

HTTP/1.1 500 Internal Server Error
Method: null
URI: null
WM_CONSUMER.ID: 16248274-1f53-4e6d-880e-b5417698b878
WM_CONSUMER.INTIMESTAMP: 
WM_QOS.CORRELATION_ID: {67F0E2F9-5EAC-4E8C-9C90-650D8F7B3B7A}
WM_SEC.AUTH_TOKEN: 
WM_SEC.REFRESH_AUTH_TOKEN: 
WM_SVC.CLASS_NAME: com.walmart.services.impl.GMPGatewayService$$EnhancerBySpringCGLIB$$c88958a9
WM_SVC.ENV: prod
WM_SVC.INTIMESTAMP: 1528364905439
WM_SVC.METHOD_NAME: doPostMultiPart
WM_SVC.NAME: Walmart Marketplace
WM_SVC.OUTTIMESTAMP: 1528364905567
WM_SVC.SERVER_IP: 10.65.34.219
WM_SVC.SERVER_NAME: PartnerGMPService-5480119-4-96141283
WM_SVC.VERSION: 
X-Powered-By: soari-interceptors-4.4.4
Content-Type: text/plain
Cteonnt-Length: 77
Server: web
Content-Encoding: gzip
Content-Length: 89
Expires: Thu, 07 Jun 2018 09:48:25 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Thu, 07 Jun 2018 09:48:25 GMT
Connection: close

And then those characters from above. Note the Cteonnt-Length: 77 header, it is not my TYPO it just comes in this way.

According to the support they have success using the following headers and ask me to use them, but I don't see any difference to what I make

WM_SVC.NAME:Walmart Marketplace
WM_QOS.CORRELATION_ID:123456abcdef
WM_SEC.AUTH_SIGNATURE:TnjevCf+voP9dmnafuCTFruwq6leBuAihSvag89WLieDRBsz7aULxgEqV71ZjIp572wVYPI07y6tdMutNLklDGwxvNdlJ2Q2xGvUIqjVPtqlhdcWvsmgqdpio7puQ4G03q1lReWzTquKecDEbB1ztH6ukj9F5rMe7d7PH8QkFsY=
WM_SEC.TIMESTAMP:1528152581896
WM_CONSUMER.ID:16248274-1f53-4e6d-880e-b5417698b878
WM_CONSUMER.CHANNEL.TYPE:0f3e4dd4-0514-4346-b39d-af0e00ea066d
Accept:application/xml
Host:marketplace.walmartapis.com
Content-Type:multipart/form-data

I already make successful communication with eBay, Amazon, PayPal, Magento and many more, but here obviously I am missing something and really don't know how to solve it.

Thank you for reading this, and hopefully someone can give a clue what is going on here.

EDIT I am adding some Delphi code as required

Version 1 without TIdMultipartFormDataStream

function TdmSyncWallmart.CreateItemFeed(aIDPlatforms: String; aLastChanged,
  aCurrDate: TDateTime; var aReqID: String): TStringStream;
var
  aXML: TNativeXML;
  aNode: TXmlNode;
  aItemNode: TXmlNode;
  aCatNode: TXmlNode;
  aTmpNode: TXmlNode;

  aShortDesc: String;
  i: Integer;
  aIDItems: String;
begin
  Result := nil;

  dstListPlatformsSyncItemsWallmart.Close;
  dstListPlatformsSyncItemsWallmart.Parameters.ParamByName('@IDPlatforms').Value := aIDPlatforms;
  dstListPlatformsSyncItemsWallmart.Parameters.ParamByName('@LastChanged').Value := aLastChanged;
  dstListPlatformsSyncItemsWallmart.Parameters.ParamByName('@CurrDate').Value := aCurrDate;
  dstListPlatformsSyncItemsWallmart.Open;

  if dstListPlatformsSyncItemsWallmart.IsEmpty then
    Exit;

  aReqID := GenGuid;

  aXML := TNativeXml.CreateName('MPItemFeed');
  try
    aXML.Declaration.AttributeAdd('standalone', 'yes');
    aXML.Root.AttributeAdd('xmlns:ns2', 'http://walmart.com/');

    //Header
    aNode := aXML.Root.NodeNew('MPItemFeedHeader');
    with aNode.NodeNew('version') do
      Value := '3.1';
    with aNode.NodeNew('requestId') do
      Value := aReqID;
    with aNode.NodeNew('mart') do
      Value := 'WALMART_US';

  //... some code for generating the XML
    Result := TStringStream.Create;
    aXML.XmlFormat := xfReadable;

    Result.WriteString('--qwerty'#13#10#13#10{'Content-Disposition: form-data; filename="wallmartreq.xml"'#13#10}+aXML.WriteToString +'--qwerty--'#13#10);
    Result.Position := 0;

  finally
    aXML.Free;
  end;
end;

function TdmSyncWallmart.CreateHTTP(aContentType: String): TIdHTTP;
begin
  Result := TIdHTTP.Create(nil);
  Result.ConnectTimeout:=5000;
  Result.ReadTimeout:=20000;
  Result.ProtocolVersion:=pv1_1;
  Result.HTTPOptions := [hoForceEncodeParams, hoKeepOrigProtocol];
  Result.HandleRedirects:=True;
  Result.IOHandler := SSLHandler;
  Result.Request.Accept:='application/xml, */*';
  Result.Request.AcceptLanguage:='en-US';
  Result.Request.ContentType:=aContentType;
//  Result.Request.CharSet:='utf-8';
  Result.Intercept := IdLogEvent1;
end;

procedure TdmSyncWallmart.AddCustomHeaders(aHTTP: TIdHTTP; aSign,
  aTimeStamp: String);
begin
  aHTTP.Request.CustomHeaders.AddValue('WM_SVC.NAME', 'Walmart Marketplace');
  aHTTP.Request.CustomHeaders.AddValue('WM_QOS.CORRELATION_ID', GenGuid);
  aHTTP.Request.CustomHeaders.AddValue('WM_SEC.TIMESTAMP', aTimeStamp);
  aHTTP.Request.CustomHeaders.AddValue('WM_SEC.AUTH_SIGNATURE', aSign);
  aHTTP.Request.CustomHeaders.AddValue('WM_CONSUMER.ID', dstPlatformsUserName.AsString);
  aHTTP.Request.CustomHeaders.AddValue('WM_CONSUMER.CHANNEL.TYPE', dstPlatformsPassword.AsString);
end;

//some code for debugging purposes
procedure TdmSyncWallmart.IdLogEvent1Send(ASender: TIdConnectionIntercept;
  var ABuffer: TIdBytes);
var
  aList: TStringList;
begin
  aList := TStringList.Create;
  try
    aList.Text := BytesToString(ABuffer);
    aList.SaveToFile('e:\wm_req_txt.txt');
  finally
    aList.Free;
  end;
end;

procedure TdmSyncWallmart.IdLogEvent1Receive(ASender: TIdConnectionIntercept;
  var ABuffer: TIdBytes);
var
  aList: TStringList;
begin
  aList := TStringList.Create;
  try
    aList.Text := BytesToString(ABuffer);
    aList.SaveToFile('e:\wm_Resp.txt');
  finally
    aList.Free;
  end;
end;

Actual call

procedure TdmSyncWallmart.SendItems(aIDPlatforms: String; aLastChanged,
  aCurrDate: TDateTime);
var
  aSign, aTimeStamp: String;
  aURL: String;
  aHTTP: TIdHTTP;
  aRes: String;
  aStrm: TMemoryStream;
  aReqID: String;

  aXML: TNativeXml;
  aNode: TXmlNode;

  aErrMsg: String;
begin
  aURL := dstPlatformsURLAddress.AsString+'/v3/feeds?feedType=item';
  GenrateSignature(aSign, aTimeStamp, aURL, 'POST');
  if aSign = '' then
    Exit;

  aHTTP := CreateHTTP('multipart/form-data;boundary=qwerty');
  AddCustomHeaders(aHTTP, aSign, aTimeStamp);
  try
    aStrm := CreateItemFeed(aIDPlatforms, aLastChanged, aCurrDate, aReqID);
    if aStrm <> nil then
    begin
      try
        aRes := TIdURI.URLDecode(aHTTP.Post(aURL, aStrm));

        aXML := TNativeXml.Create(nil);
        try
          aXML.ReadFromString(aRes);

          aNode := aXML.Root.FindNode('feedId');

          cmdUpdFeedID.Parameters.ParamByName('ID').Value := aRes;
          cmdUpdFeedID.Parameters.ParamByName('FeedID').Value := aNode.Value;
          cmdUpdFeedID.Execute;
        finally
          aXML.Free;
        end;
      except
        on E:Exception do
        begin
          if E is EIdHTTPProtocolException then
            aErrMsg := E.Message + #13#10 + (E as EIdHTTPProtocolException).ErrorMessage
          else
            aErrMsg := E.Message;

          dmMain.InsLog(cLTError, aErrMsg, aIDPlatforms);

        end;
      end;
      aStrm.Free;
    end;
  finally
    aHTTP.Free;
  end;
end;

EDIT2

procedure TdmSyncWallmart.SendItems(aIDPlatforms: String; aLastChanged,
  aCurrDate: TDateTime);
var
  aSign, aTimeStamp: String;
  aURL: String;
  aHTTP: TIdHTTP;
  aRes: String;
  aStrm: TMemoryStream;
  aReqID: String;

  aXML: TNativeXml;
  aNode: TXmlNode;

  aErrMsg: String;

  aIdStream: TIdMultipartFormDataStream;
begin
  aURL := dstPlatformsURLAddress.AsString+'/v3/feeds?feedType=item';
  GenrateSignature(aSign, aTimeStamp, aURL, 'POST');
  if aSign = '' then
    Exit;

  aHTTP := CreateHTTP('multipart/form-data');
  AddCustomHeaders(aHTTP, aSign, aTimeStamp);
  try
    aStrm := TStringStream.Create();
    begin
      try
        aIdStream := TIdMultiPartFormDataStream.Create;
        aIdStream.AddFile('file', 'e:\wm_req.xml', 'multipart/form-data');
        aIdStream.Position := 0;
        astrm.LoadFromStream(aIdStream);

        aRes := TIdURI.URLDecode(aHTTP.Post(aURL, aStrm));

        aXML := TNativeXml.Create(nil);
        try
          aXML.ReadFromString(aRes);

          aNode := aXML.Root.FindNode('feedId');

          cmdUpdFeedID.Parameters.ParamByName('ID').Value := aRes;
          cmdUpdFeedID.Parameters.ParamByName('FeedID').Value := aNode.Value;
          cmdUpdFeedID.Execute;
        finally
          aXML.Free;
        end;
      except
        on E:Exception do
        begin
          if E is EIdHTTPProtocolException then
            aErrMsg := E.Message + #13#10 + (E as EIdHTTPProtocolException).ErrorMessage
          else
            aErrMsg := E.Message;

          dmMain.InsLog(cLTError, aErrMsg, aIDPlatforms);

        end;
      end;
      aStrm.Free;
    end;
  finally
    aHTTP.Free;
  end;
end;

This is the sent data

----------060718213723831
Content-Disposition: form-data; name="file"; filename="wm_req.xml"
Content-Type: multipart/form-data
Content-Transfer-Encoding: binary

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<MPItemFeed xmlns:ns2="http://walmart.com/">
    <MPItemFeedHeader>
        <version>3.1</version>
        <requestId>{164726BF-288E-4953-A1DA-1EC7595C5725}</requestId>
        <mart>WALMART_US</mart>
    </MPItemFeedHeader>
    <MPItem>
        <processMode>REPLACE_ALL</processMode>
        <sku>w-cc-a1-4pk-white-l</sku>
        <productIdentifiers>
            <productIdentifier>
                <productIdType>UPC</productIdType>
                <productId>192082276845</productId>
            </productIdentifier>
        </productIdentifiers>
        <MPProduct>
        ..... the other part of the XML
        </MPProduct>
        <MPOffer>
            <price>25.00</price>
            <ShippingWeight>
                <measure>11.80</measure>
                <unit>lb</unit>
            </ShippingWeight>
            <ProductTaxCode>2038895</ProductTaxCode>
        </MPOffer>
    </MPItem>
</MPItemFeed>
----------060718213723831--

Returned data captured by the intercepting part is the same as the one already posted with again the Cteonnt-Length: 77 header, but I guess if I succeed in reading the gzip data, I can reach an error which can be forwarded to the support.

HTTP/1.1 500 Internal Server Error
Method: null
URI: null
WM_CONSUMER.ID: 16248274-1f53-4e6d-880e-b5417698b878
WM_CONSUMER.INTIMESTAMP: 
WM_QOS.CORRELATION_ID: {CB0208AB-2135-4A12-B45F-DD2889273971}
WM_SEC.AUTH_TOKEN: 
WM_SEC.REFRESH_AUTH_TOKEN: 
WM_SVC.CLASS_NAME: com.walmart.services.impl.GMPGatewayService$$EnhancerBySpringCGLIB$$2ca3f3b6
WM_SVC.ENV: prod
WM_SVC.INTIMESTAMP: 1528396669253
WM_SVC.METHOD_NAME: doPostMultiPart
WM_SVC.NAME: Walmart Marketplace
WM_SVC.OUTTIMESTAMP: 1528396669365
WM_SVC.SERVER_IP: 10.65.34.2
WM_SVC.SERVER_NAME: PartnerGMPService-5480119-1-96141274
WM_SVC.VERSION: 
X-Powered-By: soari-interceptors-4.4.4
Content-Type: text/plain
Cteonnt-Length: 77
Server: web
Content-Encoding: gzip
Content-Length: 89
Vary: Accept-Encoding
Expires: Thu, 07 Jun 2018 18:37:49 GMT
Cache-Control: max-age=0, no-cache, no-store
Pragma: no-cache
Date: Thu, 07 Jun 2018 18:37:49 GMT
Connection: close

EDIT3

For my Edit1, I am not adding Content-Disposition at all (it is commented) - with so many tests I have left it in my post here, so sorry for the confusion.

For Edit2 - thanks for pointing this out, actually I made my first tests without transforming into TStringStream, but I wanted to see what exactly goes to them.

Anyway now I directly used the TIdMultipartFormDataStream and changed the code in this way

    aIdStream := TIdMultiPartFormDataStream.Create;
    with aIdStream.AddFile('xml', 'e:\wm_req.xml', 'application/xml') do
    begin
      FileName := '';
      //ContentType := '';
      FileName := 'wmreq.xml';//somewhere I think I saw that only letters are allowed here
    end;
    aIdStream.Position := 0;

    aStrm := TMemoryStream.Create;

    //aRes := TIdURI.URLDecode(aHTTP.Post(aURL, aIdStream));
    aHTTP.Post(aURL, aIdStream, aStrm);

and thanks to Remy I added TIdCompressorZLib and now I get meaningful error:

HTTP/1.1 500 Internal Server Error No message body writer has been found for response class FeedAcknowledgement.

I guess this is from them, not from me, will need to check with them.

EDIT4 Solution

OK, crazy or not, maybe my problem, but once Remy said that I am not asking for compressed answer and they send me such, I decided to change // Result.Request.Accept:='application/xml, */*'; to only // Result.Request.Accept:='application/xml'; and all started working properly! In fact having */* means that I am accepting everything, isn't it? It doesn't matter if you are using TIdMultipartFormDataStream or the body is manually prepared, it just works. Thank you Remy for the different point of view, another pair of eyes always helps. Crazy ... one week of stupid tests!

  • Please show your actual code, not just the raw HTTP data. There is no `Content-Disposition: form-data; name="xml"` header in your MIME data. `TIdMultipartFormDataStream` would generate such a header. – Remy Lebeau Jun 07 '18 at 16:39
  • The "meaningless characters" is due to the response body being sent in a compressed format (`Content-Encoding: gzip`). – Remy Lebeau Jun 07 '18 at 16:39
  • And if Walmart is actually sending `Cteonnt-Length` (which I highly doubt), then you need to report that as a bug. More likely, you are simply not reading the response correctly and corrupting it. – Remy Lebeau Jun 07 '18 at 16:46
  • Right, Content-Disposition is not seen here because I showed the version where I used manually creating and sending the Payload. Please see the EDIT part. I will add EDIT2 part soon with using TIdMultipartFormDataStream – Georgi Nikolov Jun 07 '18 at 18:33
  • My point is, in the [other question](https://stackoverflow.com/questions/41604893/) you linked it, two of the answers provided use `Content-Disposition` headers. I read Walmart's API documentation, and nowhere does it say *exactly* what the HTTP requests actually need to look like, it leaves things a bit vague in that regard. Support told you to use `multipart/form-data`, but did they show you what the HTTP *BODY* should look like? When using `multipart/form-data`, `Content-Disposition` is important as it specifies a field name, which the server *may* be looking for to locate the XML. – Remy Lebeau Jun 07 '18 at 18:50
  • They showed me a pure xml file as a body part, which is a copy of what I sent them as an example. Then I asked them, since they want multipart/form-data, why they don't have Content-Disposition nor they show me boundary strings etc. and the answer was to just use the Headers they have given as example. Unfortunately nowhere in the documentation a field is specified which they expect and don't forget they actually process the feed I am sending because the items are created and I see the feed in the list of feeds received. Obviously I am the stupid here... I will check your answer now, thanks – Georgi Nikolov Jun 07 '18 at 20:08
  • "*the answer was to just use the Headers they have given as example*" - they only showed you the HEADERS to use (and not even *correct* headers, given the `Content-Type` is missing a `boundary`), but not the BODY to use. Two separate things. That is not very good support on their part. Not surprising given their documentation is just as vague. You need to ask them for *complete* HTTP examples, requests and responses, headers and bodies. If they can't give that to you, you should take your business elsewhere. – Remy Lebeau Jun 07 '18 at 20:59

1 Answers1

2

In your first Edit, you are not populating the TStringStream correctly. Use this instead:

Result.WriteString('--qwerty'#13#10{'Content-Disposition: form-data; name="file"; filename="wallmartreq.xml"'#13#10}#13#10 + aXML.WriteToString + #13#10'--qwerty--'#13#10);

In your second Edit, you are providing the wrong ContentType to TIdMultipartFormDataStream.AddFile(). You need to use application/xml instead of multipart/form-data:

aIdStream.AddFile('file', 'e:\wm_req.xml', 'application/xml');

Or, leave the ContentType blank (in which case, application/octet-stream will be used instead):

aIdStream.AddFile('file', 'e:\wm_req.xml');

If you don't want to send a ContentType at all, you can set the TIdFormDataField.ContentType property to a blank string:

with aIdStream.AddFile('file', 'e:\wm_req.xml') do
begin 
  FileName := ''; // needed so ContentType setter will not use a default value...
  ContentType := '';
  FileName := 'wm_req.xml'; // or whatever you want, or leave blank...
end;

But, more importantly, you are NOT specifying a boundary attribute for the TIdHTTP.Request.ContentType header anymore. In fact, you SHOULD NOT be saving the TIdMultipartFormDataStream to a TStringStream and then posting the TStringStream at all. You SHOULD be posting the TIdMultipartFormDataStream itself instead. That will automatically set the TIdHTTP.Request.ContentType to multipart/form-data, as well as give it a correct boundary attribute (whose value is generated by TIdMultipartFormDataStream):

aHTTP.Post(aURL, aIdStream)

Now, that being said, in both cases, you need to remove the call to TIdURI.URLDecode() on the return value of TIdHTTP.Post(). The server should be sending you raw XML, not URL-encoded XML, so you need to process the XML as-is and not URL-decode it at all.

Also, since XML is technically binary data that could be encoded in any charset that may or may not be present in the HTTP response headers, you SHOULD have your XML parser process the XML exactly as it is sent by the server. To do that, use the overloaded version of TIdHTTP.Post() that populates a TStream instead of returns a String. Have it receive the server's XML into a TMemoryStream, and then load that stream into your parser (TNativeXml has a LoadFromStream() method).


Lastly, you show that the server's response has a Content-Encoding: gzip response header, but I do not see you assigning any object to the TIdHTTP.Compressor property, or manually setting the TIdHTTP.Request.AcceptEncoding property, so the server SHOULD NOT be sending the response body in a compressed format since you are not asking for that format. TIdHTTP will not automatically decompress the data without a Compressor assigned.

If the server really is sending compressed data without being asked, that is a bug on the server side that you should report to Walmart.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • OK, I am accepting this answer, just because it pointed me the IdCompressorZLib option and more important it made me check once again what I am accepting as content type, see in EDIT4 why. – Georgi Nikolov Jun 07 '18 at 21:16
  • "*In fact having `*/*` means that I am accepting everything, isn't it?*" - Specifying `*/*` in the `Accept` header means you are willing to accept ANY media type (text, html, xml, etc) in the response (which is NOT what you want in this situation, since you are expecting XML only). Compression is completely separate from that, it is not related to media types at all. Your willingness to accept compression or not is specified by the `Accept-Encoding` header, not the `Accept` header. I still think Walmart is faulty in sending compressed data when you are not asking for compression. – Remy Lebeau Jun 07 '18 at 21:36
  • Well I mentioned all this to them, but I don't think they will consider what I am saying since they closed my case immediately. And by the way, even Content-Disposition is not necessary to be present in order all this to work. Obviously nowadays it is full of services not following normal standards, and then we (the developers) are blamed of being unable to communicate with different systems. – Georgi Nikolov Jun 07 '18 at 21:47