在Delphi 2007中对Amazon进行HTTPS POST调用

时间:2012-02-14 14:57:15

标签: delphi amazon-mws

我们使用亚马逊产品广告API在其网站上进行重新定价。对于那些不知道它的人来说,基本上可以让你在不需要花费大量时间来处理网页的情况下与Amazon数据库进行交互。它对我们很有用,或者有效。他们现在正在为卖家解散这个API,并将他们转移到他们的MWS服务上的新API。

就我所见,这些电话略有不同。两个明显的区别是PA API使用HTTP和GET,而MWS使用HTTPS和POST。

我不完全确定亚马逊文档中的示例是正确的,因为它们提供了创建哈希签名URL请求的示例。这就是我们在使用GET方法的PA-API上成功完成它的方式。但你能为POST做到这一点吗?我不认为这是可能的,虽然我可能是错的,这肯定是示例所暗示的。

无论如何,创建签名的URL不起作用!所以我决定在Delphi中使用某种低级别的HTTPS POST。

我在这里搜索过,有几个不同的例子,但我不能让它们中的任何一个正常工作。一些示例似乎使用了INDY 10,遗憾的是我们在INDY 9上遇到了(出于兼容性原因)。我还查看了WININET包装器类型函数,但除了异常或错误之外我无法获得结果。

这就是为什么我在这里寻求帮助。

如何使用Delphi对亚马逊MWS进行正确格式化的调用?我尝试了以下网页中的示例:

How to make an HTTPS POST request in Delphi?

等等我能找到的所有等等!

但我收到的错误就像'错误的请求'(例外)。

我曾尝试使用Fiddler(如其他地方所建议的)来查看正在发生的事情,但还不能完全了解它(尽管我可以在其中编写一个有效的调用!)

所以我正在寻找一些指针或方向。我真的不想升级INDY或添加新库。我宁愿保持现状并使用我现有的东西。我们正在使用D2007。

为了说明需要什么,我需要进行这种调用(GetServiceStatus是最简单的):

POST /Products/2011-10-01?AWSAccessKeyId=<ACCESSKEY>
  &Action=GetServiceStatus
  &SellerId=<SELLERID>
  &SignatureVersion=2
  &Timestamp=2012-02-14T13%3A26%3A42Z
  &Version=2011-10-01
  &Signature=dtAvv595blmv%2FnV0h2Yr5bCGzKYXid0hkOuCmZOb3bc%3D
  &SignatureMethod=HmacSHA256

到此终点:

https://mws.amazonservices.co.uk/Products/2011-10-01

我认为我的问题出现在我尝试的所有示例中,我不知道如何正确设置调用,这可能是我收到错误请求错误的原因。

因此,非常感谢一个简单的解决方案!


文档链接和观察:

这是API的开发者指南:

https://images-na.ssl-images-amazon.com/images/G/02/mwsportal/doc/en_US/bde/MWSDeveloperGuide._V161846143_.pdf

这是我们将使用的API的特定部分(取代当前的产品广告API):

https://images-na.ssl-images-amazon.com/images/G/02/mwsportal/doc/en_US/bde/MWSDeveloperGuide._V161846143_.pdf

这里最基本的功能是GetServiceStatus函数。它不需要参数。但是,它仍然需要使用来自亚马逊的凭证进行身份验证和“签名”(卖家ID,MWS访问密钥和密钥(用于生成签名)。我的感觉是,如果我能够获得最简单的功能,那么休息将会跟进。但是,身份验证的问题使得找到解决方案变得非常困难。没有测试帐户。而且时间戳(以及呼叫的签名)也会过期,只需几分钟。

还有一个“迁移指南”:

https://images-na.ssl-images-amazon.com/images/G/02/mwsportal/doc/en_US/products/MWSProductsApiMigrationGuide._V140058392_.pdf

但是这个文档确实包含一些错误。例如,结束点不正确。


我的最新代码包括ssl,cookie经理等:

var
  LHTTP                : TIdHTTP;
  IdSSLIOHandlerSocket : TIdSSLIOHandlerSocket;
  IdCookieManager      : TIdCookieManager;
  LParams              : TStringList;
  LResponse            : string;

begin
  IdCookieManager:=TIdCookieManager.Create(self);
  IdSSLIOHandlerSocket:=TIdSSLIOHandlerSocket.Create(self);
  with IdSSLIOHandlerSocket do begin
    SSLOptions.Method := sslvSSLv3;
  end;

  LHTTP := TIdHTTP.Create(Self);
  with LHTTP do begin
    CookieManager:=IdCookieManager;
    AllowCookies:=true;
    IOHandler:=IdSSLIOHandlerSocket;
    Request.ContentType:='text/xml';
    Port:=443;
    HandleRedirects:=true;
    Host:='mws.amazonservices.co.uk';
    ProtocolVersion:=pv1_1
  end;


  LParams := TStringList.Create;
  try
    LParams.Add('AWSAccessKeyId=<ACCESSKEY>');
    LParams.Add('Action=GetServiceStatus');
    LParams.Add('SellerId=<SELLERID>)');
    LParams.Add('SignatureVersion=2');
    LParams.Add('Timestamp=2012-02-15T13%3A00%3A07Z');
    LParams.Add('Version=2011-10-01');
    LParams.Add('Signature=viPlDAbzEBwlTAwq4hNaZi%2Fa1Klf7qIXIP%2BKUsOcJTI%3D');
    LParams.Add('SignatureMethod=HmacSHA256');
    LResponse:=LHTTP.Post('https://mws.amazonservices.co.uk/Products/2011-10-01?', LParams);
    ShowMessage( LResponse );
  except
    on E: Exception do
      ShowMessage('ouch! ' + E.Message );
  end;
  LHTTP.Free;
  IdSSLIOHandlerSocket.Free;
  IdCookieManager.Free;
end;

这是基于亚马逊期望根据其暂存器获得的内容。最后的用户代理是可选的,不是必需的,也不是签名过程的一部分:

POST /Products/2011-10-01?AWSAccessKeyId=<ACCESSID>
  &Action=GetServiceStatus
  &SellerId=<SELLERID>
  &SignatureVersion=2
  &Timestamp=2012-02-15T13%3A00%3A07Z
  &Version=2011-10-01
  &Signature=viPlDAbzEBwlTAwq4hNaZi%2Fa1Klf7qIXIP%2BKUsOcJTI%3D
  &SignatureMethod=HmacSHA256 HTTP/1.1
Host: mws.amazonservices.co.uk
x-amazon-user-agent: AmazonJavascriptScratchpad/1.0 (Language=Javascript)
Content-Type: text/xml

我试图从Fiddler获取信息,但它没有显示来自软件的任何流量,但它必须与Amazon通信以获得“400错误请求”错误。奇

3 个答案:

答案 0 :(得分:4)

我会使用TIdHTTP,在HTTPOptions中使用您的凭据(如果适用)进行设置,然后执行:

procedure ...
var
  LHTTP: TIdHTTP;
  LParams: TStringList;
  LResponse: string;
begin
  LHTTP := TIdHTTTP.Create;
  LParams := TStringList.Create;
  try
    // setup params, basically you're doing key-value pairs that will be encoded in the post
    // as KEY1=VALUE1&key2=value2&KEY3=value3, etc.
    // in the URL, stuff after ? are parameters
    // you don't have to worry about encoding parameters, indy will do it for you
    LParams['AWSAccessKeyId'] := '<ACCESSKEY>';
    LParams['Action'] := 'GetServiceStatus';
    LParams['SellerId'] := '<SELLERID>'
    LParams['SignatureVersion'] := '2';
    // adjust timestamp
    LParams['Timestamp'] := '2012-02-14T13%3A26%3A42Z';
    LParams['Version'] := '2011-10-01'
    // adjust signature...
    LParams['Signature'] := 'dtAvv595blmv%2FnV0h2Yr5bCGzKYXid0hkOuCmZOb3bc%3D';
    LParams['SignatureMethod'] := 'HmacSHA256';
    LResponse := LHTTP.Post('https://mws.amazonservices.co.uk/Products/2011-10-01', LParams);
    ShowMessage( LResponse );
  except
    on E: Exception do
      ShowMessage('ouch! ' + E.Message );
  end;
end;

我认为您需要将“/ Products / 2011-10-01”替换为您搜索的日期,即“/ Products / 2012-02-14”等等......

此外,如果我没有弄错,则需要SSL库...

答案 1 :(得分:1)

只是为了让每个人都知道我放弃了尝试获得这个工作的POST解决方案 - 我因为无法接近结果而浪费太多编程时间而感到非常沮丧。

相反,我回到了基础,我已经更改了字符串以签署GET而不是POST,然后创建了一个参数化的URL,然后我调用它来获取XML结果。

也许不是技术上或美学上最好的解决方案,但是,嘿,它有效!

但感谢eveyone的帮助和建议,感谢。

答案 2 :(得分:1)

只是为了让你知道,我与这个问题分享了你的痛苦,最终转到了GET格式,最后阅读了手册

使用自然字节顺序按参数名称对UTF-8查询字符串组件进行排序。参数可以 来自GET URI或POST主体(当Content-Type为application / x-www-form-urlencoded时)。

如果您在标题中设置了上述内容类型,则可以使用