How to get at response headers from THTTPReqResp?

橙三吉。 提交于 2020-04-16 08:22:53

问题


I have some old code using THTTPReqResp, this calls Execute for SOAP requests against Exchange Web Services.
I'm modifying it to (also) use OAuth login. This works fine, but I'm having trouble detecting when an access token has expired.

In a test app using a TipwHTTP component from nSoftware IP*Works I can go through the response headers to detect the one indicating an expired token:

with ipwHTTP do
  try
     PostData := ABody;
     Post(cBaseURL);
  except
      on e:Exception do
      begin
         // A special header is returned when the token has expired:
         //      Header x-ms-diagnostics: 2000002;reason="The token has expired.";error_category="invalid_lifetime"
         for l := 0 to ParsedHeaders.Count-1 do
            if (Pos('x-ms-diagnostics',ParsedHeaders[l].Field) <> 0)
            and (Pos('2000002',ParsedHeaders[l].Value) <> 0) then
            begin
               FTokenExpired := true;
               Break;
            end;
         ...   
      end;
  end;

Where/how can I access the headers when using THTTPReqResp or THTTPReqResp.HTTP?

That component contains a HTTP property of type THTTPClient but I'm getting nowhere with that. The documentation Using an HTTP Client states that THTTPClient has OnRequestError and OnRequestcompleted events but those are nowhere to be found in the code for THTTPClient or its class helper. The documentation for these events refer to System.Net.HttpClientComponent.TNetHTTPRequest not System.Net.THTTPClient, and there is no HttpClientComponent in System.Net.

Notes:

  • This is Delphi 10.3.1 Rio; in 10.3 significant code changes were made to THTTPReqResp.
  • System.Net.HttpClientComponent has a TNetHTTPClient with those two events, and a comment says it's a Component to Manage an HTTPClient, but that code does not seem to be linked in (i.e. HttpClientComponent is never used)
  • What may be related is that there is a TNetHTTPClient in the component palette (and TNetHTTPRequest, those are the only two in the Net folder), but not a THTTPClient. This seems to indicate that THTTPClient is only meant to be used 'under the hood'. TNetHTTPClient also has a HTTP (THTTPClient) property, like THTTPReqResp, but there is no inheritance between the two.

回答1:


As you have found out THTTPReqResp underwent shotgun surgery in Delphi 10.3. Originally it was using WinINet on Windows and Indy on Linux under the hood, but they changed it to THTTPClient (to support mobile platforms, I guess).

In this new implementation none of THTTPReqResp methods (Get, Execute) and events (OnBeforePost, OnReceivingData) expose Request or Response objects.

There, however, is a chance to gain access to Response object via global OnHttpError handler, if server responds with an error HTTP status code (>= 300). Use procedure Soap.SOAPHTTPTrans.SetOnHttpError to install the global handler. It has HTTPResponse: IHTTPResponse as its second parameter, which lets you inspect returned headers. If the server responds with 1xx or 2xx status then you're out of luck and should consider implementing custom THTTPReqResp descendant or migrating to more appropriate HTTP client implementation (e.g. THTTPClient directly, or similar).

{ assuming HTTPReqResp1: THTTPReqResp is a component on TForm1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  SetOnHttpError(HTTPReqRespError);
end;

procedure TForm1.HTTPReqRespError(const HTTPReqResp: THTTPReqResp;
  const HTTPResponse: IHTTPResponse; const Error: ESOAPHTTPException;
  var Action: TSOAPHttpErrorAction);
begin
  if (HTTPReqResp = HTTPReqResp1) and StartsText('2000002', HTTPResponse.HeaderValue['x-ms-diagnostics']) then
  begin
    FTokenExpired := True;
    Action := TSOAPHttpErrorAction.heaAbort; { or whatever }
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  FTokenExpired := False;
  try
    { ... }
    { HTTPReqResp1.Get or HTTPReqResp1.Execute or whatever }
    { ... }
  except
    if not FTokenExpired then
      raise;
    { handle token expiration here  }
  end;
end;

In my personal opinion this is pretty ugly way to handle such cases and I just can't understand why they introduced global handler in new code, which affects all instances of THTTPReqResp. I'm not impressed with this new design at all.

Eagle eye: Have you noticed character case inconsistency between THTTPReqResp (THTTPClient, ESOAPHTTPException) and SetOnHttpError (TSOAPHttpErrorEvent, TSOAPHttpErrorAction)?



来源:https://stackoverflow.com/questions/59666531/how-to-get-at-response-headers-from-thttpreqresp

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!