如何从THTTPReqResp获取响应头以检测OAuth令牌过期?
在Delphi 10.3.1 Rio中检测EWS OAuth令牌过期(THTTPReqResp/TNetHTTPRequest方案)
针对你遇到的问题——用THTTPReqResp做EWS OAuth请求时无法检测令牌过期,我结合Delphi 10.3的版本特性给你两个可行的方案:
核心背景说明
首先要理清一个关键混淆点:Delphi 10.3里的THTTPClient分两种:
- 底层类
System.Net.HttpClient.THTTPClient:无可视化界面,没有你在文档里看到的OnRequestError/OnRequestCompleted事件 - 可视化组件
System.Net.HttpClientComponent.TNetHTTPRequest:就是组件面板Net文件夹里的那个,它才带有事件和便捷的响应处理API
而10.3对THTTPReqResp做了重大重构,现在它内部依赖的是底层THTTPClient,但功能受限,官方更推荐直接使用TNetHTTPClient + TNetHTTPRequest组合来处理HTTP请求。
方案一:改用TNetHTTPClient + TNetHTTPRequest(推荐)
这是最灵活的方式,能直接捕获响应头并检测令牌过期:
步骤1:组件准备
在窗体上拖放TNetHTTPClient和TNetHTTPRequest,将TNetHTTPRequest.Client属性指向你的TNetHTTPClient实例。
步骤2:检测令牌过期的两种实现方式
方式A:通过OnResponseReceived事件实时检测
给TNetHTTPRequest绑定OnResponseReceived事件,在响应到达时直接遍历头信息:
procedure TYourForm.NetHTTPRequest1ResponseReceived(Sender: TObject; AResponse: IHTTPResponse); var LHeader: TNameValuePair; begin FTokenExpired := False; // 遍历所有响应头 for LHeader in AResponse.Headers do begin // 匹配目标头字段 if SameText(LHeader.Name, 'x-ms-diagnostics') then begin // 检查过期标识 if (Pos('2000002', LHeader.Value) > 0) and (Pos('The token has expired', LHeader.Value) > 0) then begin FTokenExpired := True; Break; end; end; end; end;
方式B:请求完成后直接访问响应对象
如果不需要实时处理,也可以在执行请求后直接从响应对象中提取头信息:
procedure TYourForm.SendEWSSOAPRequest(const ABaseURL, ABody: string); var LResponse: IHTTPResponse; begin try // 配置请求参数 NetHTTPRequest1.Method := rmPOST; NetHTTPRequest1.URL := ABaseURL; NetHTTPRequest1.ContentType := 'text/xml; charset=utf-8'; // EWS要求的Content-Type // 设置SOAP请求体 NetHTTPRequest1.Body.Clear; NetHTTPRequest1.Body.Add(ABody); // 执行请求并获取响应 LResponse := NetHTTPRequest1.Execute; // 手动检查令牌过期 CheckTokenExpired(LResponse); except on E: Exception do begin // 注意:EWS令牌过期通常不会抛出异常,而是返回200 OK但带错误头,所以不要只在异常块处理 ShowMessage('请求异常: ' + E.Message); end; end; end; // 单独封装检查逻辑 procedure TYourForm.CheckTokenExpired(AResponse: IHTTPResponse); var LHeader: TNameValuePair; begin FTokenExpired := False; for LHeader in AResponse.Headers do begin if SameText(LHeader.Name, 'x-ms-diagnostics') then begin if (Pos('2000002', LHeader.Value) > 0) and (Pos('The token has expired', LHeader.Value) > 0) then begin FTokenExpired := True; Break; end; end; end; end;
方案二:继续使用THTTPReqResp(兼容旧代码)
如果你不想替换旧的THTTPReqResp代码,也可以通过它的Execute方法返回的IHTTPResponse对象访问响应头:
procedure TYourForm.UseOldTHTTPReqResp(const ABaseURL, ABody: string); var LHTTPReqResp: THTTPReqResp; LResponse: IHTTPResponse; LHeader: TNameValuePair; begin LHTTPReqResp := THTTPReqResp.Create(nil); try LHTTPReqResp.URL := ABaseURL; LHTTPReqResp.Method := rmPOST; LHTTPReqResp.ContentType := 'text/xml; charset=utf-8'; LHTTPReqResp.PostData := ABody; // 执行请求并获取响应 LResponse := LHTTPReqResp.Execute; // 检查令牌过期 FTokenExpired := False; for LHeader in LResponse.Headers do begin if SameText(LHeader.Name, 'x-ms-diagnostics') then begin if (Pos('2000002', LHeader.Value) > 0) and (Pos('The token has expired', LHeader.Value) > 0) then begin FTokenExpired := True; Break; end; end; end; finally LHTTPReqResp.Free; end; end;
注意事项
EWS的令牌过期行为比较特殊:它通常不会返回4xx/5xx的错误状态码,而是返回200 OK,但在响应头中携带x-ms-diagnostics错误标识。所以不要只在异常块中检查过期,一定要在请求成功后遍历响应头。
内容的提问来源于stack exchange,提问作者Jan Doggen
相关产品推荐
相关产品推荐

