如何从THTTPReqResp获取响应头?

时间:2020-01-09 14:50:24

标签: http delphi http-headers delphi-10.3-rio

我有一些使用THTTPReqResp的旧代码,它针对Exchange Web Services的SOAP请求调用Execute
我正在将其修改为(也)使用OAuth登录。效果很好,但是我无法检测访问令牌何时过期。

在使用来自nSoftware IP * Works的TipwHTTP组件的测试应用程序中,我可以遍历响应标头来检测指示令牌已过期的标头:

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;

使用THTTPReqRespTHTTPReqResp.HTTP时在哪里/如何访问标头?

该组件包含类型为HTTP的{​​{1}}属性,但我对此一无所知。 文档Using an HTTP Client指出THTTPClient具有 OnRequestErrorOnRequestcompleted事件,但是在THTTPClient或其类帮助程序的代码中找不到这些事件。 这些事件的文档指的是THTTPClient而不是System.Net.HttpClientComponent.TNetHTTPRequest System.Net.THTTPClient中没有HttpClientComponent

注意:

  • 这是Delphi 10.3.1 Rio;在10.3中,对System.Net进行了重要的代码更改。
  • THTTPReqResp有一个包含这两个事件的System.Net.HttpClientComponent,并且注释说它是管理HTTPClient的组件,但是该代码似乎没有链接到(即从未使用过TNetHTTPClient
  • 可能相关的是,组件面板中有一个HttpClientComponent(而TNetHTTPClient是Net文件夹中仅有的两个),但没有TNetHTTPRequest。这似乎表明THTTPClient仅用于“幕后”。 THTTPClient还具有HTTP(TNetHTTPClient)属性,例如THTTPClient,但两者之间没有继承。

1 个答案:

答案 0 :(得分:1)

您已经发现THTTPReqResp在Delphi 10.3中进行了shot弹枪手术。最初,它是在Windows上使用WinINet,在Linux上是在Linux上使用Indy,但是他们将其更改为THTTPClient(我想是为了支持移动平台)。

在此新实现中,THTTPReqResp方法(GetExecute)和事件(OnBeforePostOnReceivingData)中都没有暴露RequestResponse个对象。

但是,如果服务器以错误的HTTP状态代码(> = 300)响应,则有机会通过全局Response处理程序访问OnHttpError对象。使用过程Soap.SOAPHTTPTrans.SetOnHttpError安装全局处理程序。它以HTTPResponse: IHTTPResponse作为第二个参数,可让您检查返回的标题。如果服务器以1xx或2xx状态响应,那么您不走运,应该考虑实现自定义THTTPReqResp后代或迁移到更合适的HTTP客户端实现(例如,直接THTTPClient或类似的方式)。

{ 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;

我个人认为这是处理此类情况的非常丑陋的方式,我只是不明白为什么它们在 new 代码中引入了全局处理程序,从而影响了THTTPReqResp的所有实例。我对这个新设计完全没有印象。

鹰眼:您是否注意到THTTPReqRespTHTTPClientESOAPHTTPException)和SetOnHttpErrorTSOAPHttpErrorEvent之间的字符大小写不一致,TSOAPHttpErrorAction)?