Delphi10.1:如何获取存储在REST请求(TCustomRESTRequest.body)的body参数中的JSON字符串?

Delphi10.1:如何获取存储在REST请求(TCustomRESTRequest.body)的body参数中的JSON字符串?,rest,delphi,delphi-10.1-berlin,Rest,Delphi,Delphi 10.1 Berlin,我正在实现一个REST客户端应用程序,它以JSON格式(www.Coinbase.com和)与Coinbase GDAX交易API进行通信。我在用POST签署REST请求消息时遇到问题。使用GET对REST请求消息进行签名工作正常。主要是因为GET消息没有body参数,如果可以,body参数将是签名的一部分。这个body参数让我抓狂:-)我正在努力获取REST请求(TCustomRESTRequest.body)的body参数中存储的JSON字符串。我需要这个JSON字符串来正确签署REST请求

我正在实现一个REST客户端应用程序,它以JSON格式(www.Coinbase.com和)与Coinbase GDAX交易API进行通信。我在用POST签署REST请求消息时遇到问题。使用GET对REST请求消息进行签名工作正常。主要是因为GET消息没有body参数,如果可以,body参数将是签名的一部分。这个body参数让我抓狂:-)我正在努力获取REST请求(TCustomRESTRequest.body)的body参数中存储的JSON字符串。我需要这个JSON字符串来正确签署REST请求消息。如果我将body JSON字符串传递到TCustomRESTRequest.body(难看的解决方法)之外,则会得到HTTP错误400(错误请求),并带有附加信息“无效签名”。我假设TCustomRESTRequest.Body中的JSON字符串与原始JSON字符串有所不同。我想要实现的是直接从TCustomRESTRequest.Body读取JSON字符串,然后进行签名

我在类TCoinbaseAuthenticator(继承自TCustomAuthenticator)中执行的所有身份验证和签名,或者在DoAuthenticate方法中执行的更具体的身份验证和签名:

procedure TCoinbaseAuthenticator.DoAuthenticate(ARequest: TCustomRESTRequest); 
var 
  DateTimeUnix: Int64; 
  DateTimeUnixStr: string; 
  Sign: string; 
  HttpMethod: string; 
  BodyStr: string; 
begin 
  inherited; 

   ARequest.Params.BeginUpdate; 
   try 
     DateTimeUnix := DateTimeToUnix(TTimeZone.Local.ToUniversalTime(Now)); 
     DateTimeUnixStr := IntToStr(DateTimeUnix); 

     HttpMethod := HttpMethodToString(ARequest.Method); 

     // BodyStr := ARequest.Body ... << here I'm strugging to get the JSON string 

     Sign := GenerateSignature(DateTimeUnixStr, HttpMethod, '/' + ARequest.Resource, BodyStr); 

     ARequest.AddAuthParameter('CB-ACCESS-KEY', FAPIKey, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); 
     ARequest.AddAuthParameter('CB-ACCESS-SIGN', Sign, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); 
     ARequest.AddAuthParameter('CB-ACCESS-TIMESTAMP', DateTimeUnixStr, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); 
     ARequest.AddAuthParameter('CB-ACCESS-PASSPHRASE', FAPIPassphrase, TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); 
     ARequest.AddAuthParameter('CB-VERSION', '2015-07-22', TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); 
     ARequest.AddAuthParameter('Content-Type', 'application/json', TRESTRequestParameterKind.pkHTTPHEADER, [TRESTRequestParameterOption.poDoNotEncode]); 
   finally 
     ARequest.Params.EndUpdate; 
  end; 
end; 
Coinbase(GDAX)消息签名流程的更多细节:

所有REST请求必须包含以下标头: CB-ACCESS-KEY:api密钥作为字符串(由Coinbase创建) CB-ACCESS-SIGN:base64编码签名 CB-ACCESS-TIMESTAMP:请求的时间戳 CB-ACCESS-PASSPHRASE:创建API密钥时指定的密码短语

CB-ACCESS-SIGN报头是通过使用预灰化字符串时间戳+方法(GET、POST等)+请求路径+正文(其中+表示字符串串联)上的base64解码密钥创建sha256 HMAC生成的,base64编码输出。时间戳值与CB-ACCESS-timestamp头相同

正文是请求正文字符串,如果没有请求正文(通常用于GET请求),则省略


非常感谢你的帮助

非常感谢你的评论,雷米!我确实在签名计算中使用了错误的字符集。因此,至少我的超级丑陋的解决方法(将TCustomRESTRequest.body之外的body JSON字符串传递到authenticator类)是可行的

对于问题的第一部分,我认为没有一个简单的解决办法。我得到了Embarcadero的一些支持,我想与其他有类似问题的人分享

查看用于Body属性的TBody类,它似乎只有要写入的属性—没有任何东西可以帮助读回它。我发现,如果我在TRequest中添加一个body,就会创建一个名为body的TRestRequestParameter。因此,我建议您可以通过RestRequest1.Params.ParameterByName('body')访问它

不幸的是,这项建议不起作用。原因:


参数“body”存储在body请求参数列表(TCustomRESTRequest.FBody.FParams)中,而不是存储在RESTRequest参数列表(TCustomRESTRequest.Params)中。由于TCustomRESTRequest.FBody.FParams没有公共属性,因此我无法从外部访问该字段。稍后,参数将从TCustomRESTRequest.FBody.FParams复制到TCustomRESTRequest.Params,但在我的例子中,这太晚了,因为验证器(我计算签名的地方)是在前面调用的。在我的TCoinbaseAuthenticator.DoAuthenticate(ARequest:TCustomRESTRequest)方法中,ARequest中的参数列表仍然为空。

为字符串数据生成哈希时,
CalcHMAC\u SHA256()
使用什么字符集
UnicodeString
本机为UTF-16,但通常不作为UTF-16传输。JSON通常是UTF-8。如果您使用了错误的字符集,这就可以解释“无效签名”错误。Guido,您最终解决了这个问题吗?在同一问题上苦苦挣扎。
function TCoinbaseAuthenticator.GenerateSignature(ATimeStamp: string; AMethod: string; AURL: string; ABody: string): string; 
var 
  s: string; 
  SignStr: string; 
  BitDigest: T256BitDigest; 
  key: string; 
begin 
  s := ATimeStamp+AMethod+AURL+ABody; 
  key := MimeDecodeString(FAPISecret); 
  BitDigest := CalcHMAC_SHA256(key, s); 
  SignStr := SHA256DigestAsString(BitDigest); 
  SignStr := MimeEncodeStringNoCRLF(SignStr); 
  Result := SignStr; 
end;