如何使用 TIdHttp GET 请求发送内容?

How to send content with TIdHttp GET request?

我正在尝试访问一个 REST 服务器,该服务器的端点使用 GET 动词,同时它还需要 json 在正文中发送数据

我正在尝试使用 Get 方法通过 TIdHttp class 来实现。

现在我用 json 数据创建一个 TStringStream,然后将该流分配给 TIdHttp 对象的 Request.Source 属性。

然而,服务器响应一个错误代码,表明它没有收到正文。

如何使用 TIdHttp 发送带有 GET 请求的正文?

分配 TIdHTTP.Request.Source 属性 将不起作用,因为当 TIdHTTP.Get() 调用 TIdHTTP.DoRequest() 内部传递时 Source 将被替换为 nil nil 为其 ASource 参数。

因此,要执行您要求的操作,您必须直接调用 DoRequest()。但是,它是一个 protected 方法,因此您必须使用访问器 class 才能访问它。

例如:

type
  TIdHTTPAccess = class(TIdHTTP)
  end;

var
  QueryData, ReplyData: TStream;
begin
  QueryData := TStringStream.Create('... json data here...', TEncoding.UTF8);
  try
    IdHTTP1.Request.ContentType := 'application/json';
    IdHTTP1.Request.CharSet := 'utf-8';

    //Result := IdHTTP1.Get('http://...');
    ReplyData := TMemoryStream.Create;
    try
      TIdHTTPAccess(IdHTTP1).DoRequest(Id_HTTPMethodGet, 'http://...', QueryData, ReplyData, []);
      ReplyData.Position := 0;
      Result := ReadStringAsCharset(ReplyData, IdHTTP1.Response.Charset);
    finally
      ReplyData.Free;
    end;
  finally
    QueryData.Free;
  end;
end;

正如 Remy 所建议的,您可以破解“访问器”class 以设法通过类型强制调用来访问此方法。

,你可以简单地子class TIdHttp并添加一个合适的,新的Get() 方便的方法,遵循为所有其他此类方法建立的模式。

例如类似于:

interface

  type
    TOurHttp = class(TIdHttp)
    public
      procedure Get(aUrl: String; aRequestBody, aResponseContent: TStream);
    end;

implementation

  procedure TOurHttp.Get(aUrl: String; aRequestBody, aResponseContent: TStream);
  begin
    DoRequest(Id_HttpMethodGet, aUrl, aRequestBody, aResponseContent, []);
  end;

这就是我们在我之前工作的一家公司所做的,构建 integration/middleware 软件,因为有许多示例提供的 Indy 便利方法(当时)并未涵盖我们所有的用例,不仅仅是 GET,尤其是涉及到 REST Api 时。请注意,以上不是我们当时的实际代码,我无法再访问它了。

我们没有直接使用 TIdHttp class,而是使用了 TOurHttp(不是真实姓名),它被赋予了许多对我们有用的额外便利方法。我们还发现我们必须更改和扩展其他方面,例如 IgnoreReplies 行为,以适应各种 'rogue' REST Api 服务器行为(并非所有行为都符合 HTTP 标准) 在大多数用例中你也找不到。

当然,您可以尝试要求 Api 或 HTTP 服务器的提供商更改 他们的 端,而不是修复您的客户端行为。祝你好运。 ;)

背景阅读

HTTP 协议规范确实允许 GET 请求提交请求正文,但绝大多数实现不允许(或干脆忽略任何此类内容)。这可能是因为从历史上看,绝大多数实现都针对浏览器行为或由浏览器行为驱动,再加上 HTTP 规范不幸地在该领域允许了一些“回旋余地”。

这导致了我们今天所处的位置,在撰写本文时(2019 年 1 月)HTTP 规范的相关部分 RFC7231 has this to say about content in a GET request

A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request.

即请求内容 允许的,因为它没有被明确禁止并且它的使用被认为是可能的并且有时是预期的(并且只是有时被拒绝)。但是您不能依赖服务器对此类请求采用任何特定解释(甚至接受)。

这当然无助于处理 决定其特定解释的特定服务器的客户端。通过以这种方式向错误的做法让步,HTTP 规范的这种迭代就没有多大用处了;它只是描述了实践中的差异,而不是提供可以针对 compliance/accuracy.

测试实现的规范