gpt4 book ai didi

delphi - TRestClient/TRestRequest错误地解码了gzip响应

转载 作者:行者123 更新时间:2023-12-03 14:59:54 26 4
gpt4 key购买 nike

我试图阅读REST API,该API是gzip编码的。确切地说,我尝试阅读StackExchange API。

我已经找到了Automatically Decode GZIP In TRESTResponse?问题,但是由于某种原因该答案无法解决我的问题。

测试设置

在XE5中,我添加了具有以下相关属性的TRestClient,TRestRequest和TRestResponse。我设置了客户端的BaseURL,请求的资源和参数,并将请求的AcceptEncoding设置为gzip, deflate,这应该使其能够自动解码压缩的响应。

  object RESTClient1: TRESTClient
BaseURL = 'https://api.stackexchange.com/2.2'
end
object RESTRequest1: TRESTRequest
AcceptEncoding = 'gzip, deflate'
Client = RESTClient1
Params = <
item
Kind = pkURLSEGMENT
name = 'id'
Options = [poAutoCreated]
Value = '511529'
end
item
name = 'site'
Value = 'stackoverflow'
end>
Resource = 'users/{id}'
Response = RESTResponse1
end
object RESTResponse1: TRESTResponse
end

结果是URL:

https://api.stackexchange.com/2.2/users/511529?site=stackoverflow



我这样调用请求,并带有两个消息框以显示URL和请求的结果:
ShowMessage(RESTRequest1.GetFullRequestURL());
RESTRequest1.Execute; // Actual call
ShowMessage(RESTResponse1.Content);

如果我在浏览器中调用该URL,则会得到正确的结果,这是一个包含我的一些用户信息的json对象。

问题

但是,在Delphi中,我没有得到JSON响应。实际上,我得到了一堆字节,似乎是gzip响应错误。我尝试用 TIdCompressorZlib.DecompressGZipStream()解压缩,但失败了,但使用 ZLib Error (-3)。当我自己检查响应的字节时,我看到它以#1F#3F#08开头。这特别奇怪,因为gzip header 应为#1F#8B#08,所以#8B转换为#3F,这是一个问号。

因此在我看来,RESTClient试图对gzip流进行解码,就好像它是UTF-8响应一样,并用问号代替了无效序列(#8B本身不是有效的UTF-8字符)。

尝试(肤浅)

我已经做了很多实验,例如
  • 使用RESTResponse.RawBytes并尝试对其进行解码。我注意到此字节数组中的字节已经无效。 TRESTResponse来源中的注释告诉我,“RawBytes”已被解码,因此这很有意义。
  • 将RESTResponse.RawBytes保存在文件中,并尝试使用7zip和几个在线gzip解压缩器对其进行解压缩。当然,它们都失败了,因为即使gzip header 也不正确。
  • 将值'gzip,deflate'分配给TRESTClient.AcceptEncoding,TRESTResponse.AcceptEncoding以及它们的组合。还尝试将其附加到每个组件的预填充Accept属性。
  • 从经过身份验证的请求切换为未经身份验证的请求。我已经完成了整个oAuth部分的工作,但是尽管这样会使问题变得过于复杂。不过,我在此问题中使用的匿名API也存在相同的问题。

  • 不幸的是,它仍然无法正常工作,我仍然收到了错误的答复。

    尝试次数(深入VCL)

    最终,我更深入地研究了TRestRequest.Execute。我不会在此处粘贴所有代码,但最终它会通过调用来执行请求
    FClient.HTTPClient.Get(LURL, LResponseStream);

    FClient是链接到请求的TRESTClient,而LResponseStream是TMemoryStream。我在 watch 上添加了 LResponseStream.SaveToFile('...'),因此它将保存此未处理的结果,等等,它为我提供了一个有效的gz文件,可以将其解压缩以获取JSON。

    解决方法中的错误?

    但是,接下来几行,我看到了这段代码:
      if FClient.HTTPClient.Response.CharSet > '' then
    begin
    LResponseStream.Position := 0;
    S := FClient.HTTPClient.ReadStringAsCharset(LResponseStream, FClient.HTTPClient.Response.CharSet);
    LResponseStream.Free;
    LResponseStream := TStringStream.Create(S);
    end;

    根据此块上方的注释,之所以这样做,是因为内存流的内容“未根据可能存在的Encoding或Content-Type Charset参数进行相应的编码”,此VCL代码的编写者将其视为Indy中的错误。 。

    因此,基本上,这里发生了什么:原始响应被视为字符串,并转换为“正确”的编码。 FClient.HTTPClient.Response.CharSet是'UTF-8',这确实是JSON的编码,但是不幸的是,这种转换只能在解压缩流之后完成,但尚未完成。因此,我认为这是一个错误。 ;)

    我试图进行更深入的研究,但是我找不到应该进行减压的地方。实际的请求由IIPHTTP实例执行,该实例是IPPeerAPI.dcu,我没有源。

    所以...

    所以我的问题是双重的:
  • 为什么会发生这种情况?当您将AcceptEncoding设置为'gzip,deflate'时,TRestClient应该会自动解码gzip流。我错过了什么设置?还是XE5还不支持此功能?
  • 如何防止gzip流的这种错误翻译?我不介意自己解码响应,只要它可以工作,尽管理想情况下REST组件应该自动执行。

  • 我的设置:VCL Forms应用程序,Windows 8.1,Delphi XE5 Professional Update 2。

    更新
  • 已找到解决方法(请参阅我的答案)
  • 质量报告
  • 中的错误报告 RSP-9855
  • 据说它已在Delphi 10.1(Berlin)中修复,但我尚未对此进行测试。
  • 最佳答案

    雷米·勒博(Remy Lebeau)对这个问题的回答以及对Automatically Decode GZIP In TRESTResponse?问题的回答的评论使我走上了正确的道路。

    就像他说的那样,设置AcceptEncoding是不够的,因为执行实际请求的TIdHTTP没有附加解压缩器,因此无法解压缩gzip响应。基于稀疏资源,我想到了设置AcceptEncoding也会自动解压缩响应的想法,但是这个想法是错误的。

    尽管如此,在这种情况下,将AcceptEncoding留为空白也不起作用,因为与之相关的API(即StackExchange API)为always compressed,无论您是否指定接受gzip。

    因此,a)始终压缩的响应,b)无法解压缩的HTTP客户端和c)TRESTRequest对象(不正确地假定响应已经正确解压缩)的组合会导致这种情况。

    我仅看到两种解决方案,第一种是完全丢弃TRESTClient并仅使用简单的TIdHTTP执行请求。遗憾的是,因为我的目标是探索新的REST组件的可能性,以了解它们如何使生活更轻松。

    因此,另一种解决方案是将压缩器分配给内部使用的TIdHTTP。

    我成功地取得了成功,尽管不幸的是,它消除了TREST组件试图引入的许多抽象概念。这是解决它的代码:

    var
    Http: TIdCustomHTTP;
    begin
    // Get the TIdHTTP that performs the request.
    Http := (RESTRequest1 // The TRESTRequest object
    .Client // The TRESTClient
    .HTTPClient // A TRESTHTTP object that wraps HTTP communication
    .Peer // An IIPHTTP interface which is obtained through PeerFactory.CreatePeer
    .GetObject // A method to get the object instance of the interface
    as TIdCustomHTTP // The object instance, which is an TIdCustomHTTP.
    );

    // Attach a gzip decompressor to it.
    Http.Compressor := TIdCompressorZLib.Create(Http);

    之后,我可以使用RESTRequest1组件成功获取JSON响应(至少作为文本)。

    关于delphi - TRestClient/TRestRequest错误地解码了gzip响应,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27607506/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com