gzip으로 인코딩 된 REST API를 읽으려고했습니다. 정확히 말하면 StackExchange API를 읽으려고했습니다.TRestClient/TRestRequest가 gzip 응답을 잘못 디코딩합니다.
나는 이미 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에 결과 :
: URL과 요청의 결과를 보여주기 위해 두 개의 메시지 상자,https://api.stackexchange.com/2.2/users/511529?site=stackoverflow
는이 같은 요청을 호출
ShowMessage(RESTRequest1.GetFullRequestURL());
RESTRequest1.Execute; // Actual call
ShowMessage(RESTResponse1.Content);
브라우저에서 해당 URL을 호출하면 적절한 r 내 사용자 정보가있는 json 객체 인 esult.
문제
그러나, 델파이, 나는 JSON 응답을하지 않습니다. 사실 이으로 보이는 바이트 묶음이 mangled gzip 응답으로 표시됩니다. TIdCompressorZlib.DecompressGZipStream()
으로 압축을 풀려고했으나 ZLib Error (-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('...')
을 추가 했으므로이 처리되지 않은 결과를 저장합니다. et voilá, 유효한 JSF 파일을 얻었습니다.이 파일을 압축 해제하여 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;
:
그러나 한 다음, 아래 라인의 몇 가지, 나는이 코드 조각을 참조 따라서이 VCL 코드의 작성자가 Indy의 버그로 간주하는 가능한 Encoding 또는 Content-Type Charset 매개 변수에 따라 달라집니다.
그래서 기본적으로 원시 응답은 문자열로 처리되어 '올바른'인코딩으로 변환됩니다. FClient.HTTPClient.Response.CharSet은 실제로 JSON의 인코딩 인 'UTF-8'이지만, 아직 완료되지 않은 스트림의 압축을 푼 후에 만이 변환을 수행해야합니다. 그래서 이것은 나에 의해 버그로 간주됩니다. ;)
나는 더 깊게 파고 들려고했지만이 감압이 일어난 장소를 찾을 수 없었다. 실제 요청은 소스가없는 IPPeerAPI.dcu 인 IIPHTTP 인스턴스에 의해 수행됩니다. 그래서
...
그래서 제 질문은 두 가지이다 :
- 왜 이런 일이 무엇입니까? AcceptEncoding을 'gzip, deflate'로 설정하면 TRestClient가 자동으로 gzip 스트림을 디코딩해야합니다. 어떤 설정을 놓쳤습니까? 또는 XE5에서 아직 지원되지 않습니까?
- gzip 스트림의이 잘못된 번역을 어떻게 방지합니까? 이상적으로는 REST 구성 요소가 자동으로 처리해야한다고는해도 응답을 직접 해독해도 상관 없습니다.
내 설정 : VCL 애플리케이션, 윈도우 8.1, 델파이 XE5 전문 업데이트 2.
업데이트 양식
- 해결 방법 (내 대답을 참조) 발견
- 버그 보고서 RSP-9855 품질 중앙 출원
- Delphi 10.1 (Berlin)에서 수정되었지만 아직 테스트하지 않았습니다.=
감사하지만, 불행히도 완전히 사실이 아닙니다. 어쩌면 내가 이것에 대해 너무 명확하지는 않지만 AcceptEncoding을 설정하는 것은 이미이 문제를 해결하기위한 시도였습니다. 처음에는 그 일을하지 않았고 여전히 같은 문제가있었습니다. 내가 게시 한 스 니펫은 결과 스트림 ('Content' 속성과'RawBytes' 속성'모두)을 항상 변환하려고합니다. 'encoding'이 'gzip'이라는 사실은 완전히 무시되며 결과 스트림은 항상 Response에 할당되기 전에 처리되므로 RawBytes에도 영향을 미칩니다. 처리되지 않은 실제 응답은 Execute 메서드 내에서 이미 구분됩니다. – GolezTrol
'TRESTClient' 논리 버그와 유사합니다 (TIdHTTP 버그가 아닙니다). Embarcadero에 신고 했습니까? 어쨌든'AcceptEncoding'이 설정되어 있지 않다면, 서버는 인코딩되지 않은 실제 JSON을 보내야합니다. TRESTClient는 그것을'String'으로 디코딩합니다. 이것이 올바르게 디코딩되지 않으면, 지정된'charset'이 틀릴 가능성이 있습니다. 서버에서 전송중인 실제 REST 응답을 표시 할 수 있습니까? –
TRESTClient 버그 인 것 같습니다. 나는 (아직) 그것을보고하지 않았고, 그것이 얼마나 유용한 지 잘 모르겠습니다. 나는 그들이 XE5에 대한 업데이트를한다고 생각하지 않는다. 하지만 문제는 새로운 버전에서도 존재할 수 있으므로 고려해 보겠습니다. – GolezTrol