加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-15 09:31:40 所属栏目:大数据 来源:网络整理
导读:我试图读取一个REST API,它是gzip编码的.确切地说,我试图阅读StackExchange API. 我已经找到了问题Automatically Decode GZIP In TRESTResponse?,但由于某种原因,这个答案并没有解决我的问题. 测试设置 在XE5中,我添加了TRestClient,TRestRequest和具有以下
我试图读取一个REST API,它是gzip编码的.确切地说,我试图阅读StackExchange API.

我已经找到了问题Automatically Decode GZIP In TRESTResponse?,但由于某种原因,这个答案并没有解决我的问题.

测试设置

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

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:

07001

我调用这样的请求,有两个消息框来显示url和请求的结果:

ShowMessage(RESTRequest1.GetFullRequestURL());
RESTRequest1.Execute; // Actual call
ShowMessage(RESTResponse1.Content);

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

问题

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

所以在我看来,RESTClient试图将gzip流解码为好像是UTF-8响应,并且用问号替换了无效序列(#8B本身不是有效的UTF-8字符).

尝试(表面)

我做了很多实验,比如

>使用RESTResponse.RawBytes并尝试解码它.我注意到这个字节数组中的字节已经无效. TRESTResponse来源中的评论告诉我’RawBytes’已经被解码,所以这是有道理的.
>将RESTResponse.RawBytes保存在一个文件中,并尝试使用7zip和几个在线gzip解压缩程序对其进行解压缩.当然,它们都失败了,因为即使是gzip标头也是错误的.
>将值’gzip,deflate’分配给TRESTClient.AcceptEncoding,TRESTResponse.AcceptEncoding及其组合.还尝试将其附加到每个组件的预填充Accept属性.
>从经过身份验证的请求切换到未经身份验证的请求.我有整个oAuth部分工作,但我认为这会使问题过于复杂.我在这个问题中使用的匿名API也有同样的问题.

不幸的是它仍然无效,我仍然得到了错误的回应.

尝试(深入VCL)

最后,我挖了一点,然后潜入TRestRequest.Execute.我不会在这里粘贴所有代码,但最终它会通过调用来执行请求

FClient.HTTPClient.Get(LURL,LResponseStream);

FClient是链接到请求的TRESTClient,LResponseStream是TMemoryStream.我将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的编码,但不幸的是,这种转换只能在解压缩流之后才能完成,但尚未完成.所以我认为这是一个错误.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读