3

我已经在这个问题上工作了太多小时而没有真正取得任何进展。我有一个有效的旧解决方案,但我正在尝试将其移植到 Indy 以使代码更可靠且更易于维护。

我们有一个 servlet,它处理使用 HTTP POST 消息发送给它的请求。消息有一个参数,“command”,它的值决定了 servlet 应该做什么。

目前我有这个:

procedure TIndyLoginServer.SendRequest(Command, Json: string; Request: TRequest);
var
  Params: TIdStrings;
  ServerResponse: string;
begin
  // Build parameters
  Params := TStringList.Create();
  Params.Add('command=' + Command);

  try
    // Content type should really be 'application/json' but then the parameters stop working
    FIndyHttp.Request.ContentType := 'application/x-www-form-urlencoded'; 
    ServerResponse := FIndyHttp.Post(FUrl, Params);
    Request.OnRequestFinished(ServerResponse, '', '');
  except
    on E: EIdHTTPProtocolException do
    begin
      Request.OnRequestFinished('', E.Message, '');
    end;
    on E: EIdSocketError do
    begin
      if E.LastError = Id_WSAETIMEDOUT then      
        Request.OnRequestTimedOut();
    end;
    on E: EIdException do
    begin
      Request.OnRequestFinished('', E.Message, '');
    end;
  end;
end;

这种工作,命令到达 servlet 并开始按预期工作。问题是,除了参数之外,我还需要能够将 JSON 编码的对象与 POST 请求一起发送。Java servlet 使用以下代码行接收 JSON 编码的对象

final BufferedReader r = req.getReader();

“req”显然是传入的 POST 请求,缓冲的读取器稍后用于解码对象。但是,我一生都无法弄清楚如何以 servlet 可以读取数据的方式将 JSON 字符串附加到 TidHTTP 实例。

有没有人有任何建议或例子我可以看看?我所发现的只是如何发送文件。也许这就是我要找的?

以及如何在不破坏参数列表的情况下将请求的内容类型设置为“application/json”?如果我更改它,POST 请求仍会到达服务器,但不再找到“命令”参数。

4

2 回答 2

3

主要是一个提示:当您使用“application/x-www-form-urlencoded”时,服务器端希望看到键/值对,例如:

key1=value1&key2=value2 etc.

在这种情况下,您可以将 JSON 文本作为键的值发送,例如:

{...}
Params.Add('command=' + Command);
Params.Add('jsonText=' + json);
FIndyHttp.Request.ContentType := 'application/x-www-form-urlencoded'; 
ServerResponse := FIndyHttp.Post(FUrl, Params);
{...}

使用“application/json”对 HTTP 服务器说:请求的整个主体(标头之后的部分)将包含 JSON 文本。

在 PHP 中,我可以使用“输入流”访问请求的“正文”:

$jsonText = file_get_contents("php://input")
$jsonObject = json_decode($jsonText )

尝试在 JAVA 中找到类似的东西?

更新:

似乎 TIdStrings 将请求准备为“application/x-www-form-urlencoded”。正如Remy Lebeau 建议的那样,将您的 JSON 字符串保存到 TStream 后代(例如 tMemoryStream)并按原样传输(即 RAW)。tIdHttp.POST 有一个接受 TStream 的覆盖。在这种情况下尝试 TIdHTTP.Request.ContentType = 'application/json'。它应该这样工作吗?

于 2013-04-18T15:08:53.713 回答
2

我想我解决了。我不确定这是最漂亮的方式,但它似乎可以满足我的要求。

这是代码!

procedure TIndyLoginServer.SendRequest(Command, Json: string;
  Request: TRequest);
var
  JsonToSend: TIdStringStream;
  ServerResponse: string;
  Url: string;
begin
  // Build parameters
  JsonToSend := TIdStringStream.Create(Json);

  try
    try
      FIndyHttp.Request.Accept := 'application/json';
      FIndyHttp.Request.ContentType := 'application/json';
      FIndyHttp.Request.ContentEncoding := 'utf-8';


      Url := FUrl +'?command=' + Command;
      ServerResponse := FIndyHttp.Post(Url, JsonToSend);

      Request.OnRequestFinished(ServerResponse, '', '');
    except
      on E: EIdHTTPProtocolException do
      begin
        Request.OnRequestFinished('', E.Message, '');
      end;
      on E: EIdSocketError do
      begin
        if E.LastError = Id_WSAETIMEDOUT then
          Request.OnRequestTimedOut();
      end;
      on E: EIdException do
      begin
        Request.OnRequestFinished('', E.Message, '');
      end;
    end;
  finally
    JsonToSend.Free();
  end;
end;
于 2013-04-18T15:54:27.557 回答