【问题标题】:POST sent by Delphi 5 broke my GraphQL PHP APIDelphi 5 发送的 POST 破坏了我的 GraphQL PHP API
【发布时间】:2018-09-11 19:25:06
【问题描述】:

我在 PHP 中创建了一个简单的 API,用于从 GET/POST 请求中接收一些变量并将其发布到第三方 GraphQL API。但我在内容编码方面遇到了问题。

应用程序以这种方式工作:

Delphi 5 桌面应用程序向我的 PHP API 发送带有一些变量的简单 GET 或 POST,我的 PHP API 向第三方 GraphQL API 发出查询请求。

我发送的 GraphQL 是:

query {
  node(id: 1){
    ... on Organization{
      fullname
      entities(type: STUDENT, search: "Some Student Name"){
        nodes{
          dbId
          fullname
          eid
        }
      }
    }
  }
}

请求网址示例为:

http://api-link/request.php?token=xyzwsa&id=17&tipo=boleto&mat=123456&nome_aluno=Some%20Name%20Here&nome_responsavel=Another%20Name&titulo=Just%20a%20test&numero=001122&venc=2018-04-02&valor=1000&linha=23794.00000%2000000.000000%2000000.000000%200%2000000000000000

当我将请求 URL 复制并粘贴到浏览器时,在命令提示符 cURL 上调用它,工作正常。

但是,当 Delphi 5 调用它时,它用很多“+”(加号)破坏了我的查询字符串:

Syntax Error GraphQL request (1:6) Cannot parse the unexpected character "+".

1: query+%7B%0D%0A++node%28id%3A+1%29%7B%0D%0A++++...+on+Organization%7B%0D%0A++++++fullname%0D%0A++++++entities%28type%3A+STUDENT%2C+search%3A+%22Some Student Name%22%29%7B%0D%0A++++++++nodes%7B%0D%0A++++++++++dbId%0D%0A++++++++++fullname%0D%0A++++++++++eid%0D%0A++++++++%7D%0D%0A++++++%7D%0D%0A++++%7D%0D%0A++%7D%0D%0A%7D
        ^

Delphi 5 应用程序使用 TidHTTP 组件来完成它。代码如下:

procedure TForm1.ExportaClassApp(prID : Integer; prNumBoleto, prLinhaDigitavel : String);
const cUrl         = 'http://api-link/request.php?';
      cToken       = 'xyzwsa';
      cTipo        = 'boleto';
      cE_Comercial = #138; (* equivalent to & *)

var sSQL              : String;
    qryPesquisa       : TADOQuery;
    oHTTP             : TIdHTTP;
    sRetornoClassApp  : String;
    HTTPClient        : TidHTTP;
    Lista             : TStringStream;
    sParametros       : String;

    Url_Completa      : String;
    sToken            : String;
    sId               : String;
    sTipo             : String;
    sMatricula        : String;
    sNome_Aluno       : String;
    sNome_Responsavel : String;
    sDescricao        : String;
    sNumBoleto        : String;
    sVencimento       : String;
    sValor            : String;
    sLinhaDigitavel   : String;
begin
   oHTTP      := TIdHTTP.Create(Application);
   HTTPClient := TidHTTP.Create(Application);;
   sDescricao := '';

   sDescricao := Copy(sDescricao,1, length(sDescricao) - 2);

   sToken            := 'token='             + cToken;
   sId               := '&id='               + IntToStr(prId);
   sTipo             := '&tipo='             + cTipo;
   sMatricula        := '&mat='              + '123456';
   sNome_Aluno       := '&nome_aluno='       + 'Some Student Name';
   sNome_Responsavel := '&nome_responsavel=' + 'Another Name';
   sDescricao        := '&titulo='           + 'Just a test';
   sNumBoleto        := '&numero='           + prNumBoleto;
   sVencimento       := '&venc='             + '2018-04-02';
   sValor            := '&valor='            + FloatToStr(843 * 100);
   sLinhaDigitavel   := '&linha='            + prLinhaDigitavel;

   sParametros  := sToken + sId + sTipo + sMatricula + sNome_Aluno + sNome_Responsavel + sDescricao + sNumBoleto + sVencimento + sValor + sLinhaDigitavel;
   Url_Completa := cUrl + sPArametros;
   Url_Completa := StringReplace(Url_Completa,' ','%20',[rfReplaceAll, rfIgnoreCase]);

    if edtContentType.Text <> '' then
       oHTTP.Request.ContentType := edtContentType.Text
    else
       oHTTP.Request.ContentType := '';

    if edtContentEncoding.Text <> '' then
       oHTTP.Request.ContentEncoding := edtContentEncoding.Text
    else
       oHTTP.Request.ContentEncoding := '';    

   sRetornoClassApp := oHTTP.URL.URLDecode(oHTTP.Get(Url_Completa));

   btnLimparClick(Self);

   mmoEnvio.Text   := Url_Completa;
   mmoRetorno.Text := sRetornoClassApp;

   FreeAndNil(oHTTP);
end;

这是浏览器/cURL 请求/响应标头(有效):

Array
(
    [0] => POST /graphql HTTP/1.1
    [1] => Host: joy.classapp.co
    [2] => User-Agent: PHP Curl/1.6 (+https://github.com/php-mod/curl)
    [3] => Accept: */*
    [4] => Content-Length: 400
    [5] => Content-Type: application/x-www-form-urlencoded
)
Array
(
    [0] => HTTP/1.1 200 OK
    [1] => Access-Control-Allow-Origin: *
    [2] => Content-Type: application/json; charset=utf-8
    [3] => Date: Mon, 02 Apr 2018 14:37:19 GMT
    [4] => ETag: W/"4d-r2dRUM/0NEHToQUzFDAesWSzSWY"
    [5] => Server: nginx/1.12.1
    [6] => X-Content-Type-Options: nosniff
    [7] => X-DNS-Prefetch-Control: off
    [8] => X-Download-Options: noopen
    [9] => X-Frame-Options: SAMEORIGIN
    [10] => X-XSS-Protection: 1; mode=block
    [11] => Content-Length: 77
    [12] => Connection: keep-alive
)

在 Delphi 5 TidHTTP 请求/响应标头下方(不起作用):

Array
(
    [0] => POST /graphql HTTP/1.1
    [1] => Host: joy.classapp.co
    [2] => User-Agent: PHP Curl/1.6 ( https://github.com/php-mod/curl)
    [3] => Accept: */*
    [4] => Content-Length: 407
    [5] => Content-Type: application/x-www-form-urlencoded
)
Array
(
    [0] => HTTP/1.1 400 Bad Request
    [1] => Access-Control-Allow-Origin: *
    [2] => Content-Type: application/json; charset=utf-8
    [3] => Date: Mon, 02 Apr 2018 14:47:53 GMT
    [4] => ETag: W/"1f6-RtlkHyZy4MNsaj0EjU9LCzebnSs"
    [5] => Server: nginx/1.12.1
    [6] => X-Content-Type-Options: nosniff
    [7] => X-DNS-Prefetch-Control: off
    [8] => X-Download-Options: noopen
    [9] => X-Frame-Options: SAMEORIGIN
    [10] => X-XSS-Protection: 1; mode=block
    [11] => Content-Length: 502
    [12] => Connection: keep-alive
)

我尝试更改 Content-Type em Curl Class(我正在使用 Curl/Curl 库)以使 POST 到 GraphQL API,但不成功。

我不知道为什么它在浏览器/cURL 中完美运行,而不是在 Delphi 5 中。 问题出在我的 PHP API 或 Delphi 5 TidHTTP 组件中?还是两者兼有?

编辑:使用 GET 和 POST http 动词尝试了 Delphi 5 和 Delphi 2006 以及 Indy v10.1.5。同样的错误。

感谢关注。

【问题讨论】:

  • 请显示不适合您的实际 Delphi 代码。 Delphi 5 非常老旧,您使用的是 Delphi 5 附带的 Indy 版本,还是使用最新的现代版本?
  • @RemyLebeau 使用 GET 和 POST http 动词尝试了 Delphi 5 和 Delphi 2006 以及 Indy v10.1.5。同样的错误。我用 Delphi 代码编辑了我的帖子。
  • 为什么一开始就使用这些过时的版本? Delphi 5 已经快 20 岁了,而 Indy 10.1.5 也差不多这么老了。当前的 Indy 版本是 10.6.2。在过去十年中,TIdHTTP 发生了许多更改和修复。
  • 另外,您遇到的失败是在 POST 请求上,但您的 Delphi 代码正在发送 GET 请求。您没有显示 POST 正文的内容,或接收 GET 然后生成 POST 的 PHP 代码。所以有几个可能的故障点,但您没有提供足够的诊断数据来确定哪一个实际发生故障。

标签: php delphi curl graphql


【解决方案1】:

您的 Delphi 代码存在一些问题。

  • 您正在实例化 2 个TIdHTTP 对象,但只使用其中一个并泄漏另一个。

  • 您应该使用 TIdURI 来格式化 URL 查询参数,而不是 StringReplace()

  • 您不需要也不应该使用TIdURI 来解码TIdHTTP.Get() 的结果。只需按原样使用结果即可。

试试这个:

procedure TForm1.ExportaClassApp(prID : Integer; prNumBoleto, prLinhaDigitavel : String);
const
  cUrl         = 'http://api-link/request.php?';
  cToken       = 'xyzwsa';
  cTipo        = 'boleto';
var
  oHTTP             : TIdHTTP;
  sToken            : String;
  sId               : String;
  sTipo             : String;
  sMatricula        : String;
  sNome_Aluno       : String;
  sNome_Responsavel : String;
  sDescricao        : String;
  sNumBoleto        : String;
  sVencimento       : String;
  sValor            : String;
  sLinhaDigitavel   : String;
  sParametros       : String;
  Url_Completa      : String;
  sRetornoClassApp  : String;
begin
  sToken            := 'token='             + cToken;
  sId               := '&id='               + IntToStr(prId);
  sTipo             := '&tipo='             + cTipo;
  sMatricula        := '&mat='              + '123456';
  sNome_Aluno       := '&nome_aluno='       + TIdURI.ParamsEncode('Some Student Name');
  sNome_Responsavel := '&nome_responsavel=' + TIdURI.ParamsEncode('Another Name');
  sDescricao        := '&titulo='           + TIdURI.ParamsEncode('Just a test');
  sNumBoleto        := '&numero='           + TIdURI.ParamsEncode(prNumBoleto);
  sVencimento       := '&venc='             + '2018-04-02';
  sValor            := '&valor='            + FloatToStr(843 * 100);
  sLinhaDigitavel   := '&linha='            + TIdURI.ParamsEncode(prLinhaDigitavel);

  sParametros  := sToken + sId + sTipo + sMatricula + sNome_Aluno + sNome_Responsavel + sDescricao + sNumBoleto + sVencimento + sValor + sLinhaDigitavel;
  Url_Completa := cUrl + sParametros;

  oHTTP := TIdHTTP.Create(nil);
  try
    oHTTP.Request.ContentType := edtContentType.Text;
    oHTTP.Request.ContentEncoding := edtContentEncoding.Text;
    sRetornoClassApp := oHTTP.Get(Url_Completa);
  finally
    oHTTP.Free;
  end;

  btnLimparClick(Self);

  mmoEnvio.Text   := Url_Completa;
  mmoRetorno.Text := sRetornoClassApp;
end;

或者:

procedure TForm1.ExportaClassApp(prID : Integer; prNumBoleto, prLinhaDigitavel : String);
const
  cUrl         = 'http://api-link/request.php?';
  cToken       = 'xyzwsa';
  cTipo        = 'boleto';
var
  oHTTP             : TIdHTTP;
  sParametros       : String;
  Url_Completa      : String;
  sRetornoClassApp  : String;
begin
  sParametros  := Format('token=%s&id=%d&tipo=%s&mat=%s&nome_aluno=%s&nome_responsavel=%s&titulo=%s&numero=%s&venc=%s&valor=%f&linha=%s',
    [cToken,
     prId,
     cTipo,
     '123456',
     TIdURI.ParamsEncode('Some Student Name'),
     TIdURI.ParamsEncode('Another Name'),
     TIdURI.ParamsEncode('Just a test'),
     TIdURI.ParamsEncode(prNumBoleto),
     '2018-04-02',
     843 * 100,
     TIdURI.ParamsEncode(prLinhaDigitavel)]
  );

  Url_Completa := cUrl + sParametros;

  oHTTP := TIdHTTP.Create(nil);
  try
    oHTTP.Request.ContentType := edtContentType.Text;
    oHTTP.Request.ContentEncoding := edtContentEncoding.Text;
    sRetornoClassApp := oHTTP.Get(Url_Completa);
  finally
    oHTTP.Free;
  end;

  btnLimparClick(Self);

  mmoEnvio.Text   := Url_Completa;
  mmoRetorno.Text := sRetornoClassApp;
end;

【讨论】:

    【解决方案2】:

    我们发现这个问题只发生在带有重音的字符串上。 所以,经过一番研究,我们found a solution(可能不是最优雅的)将 ASCII 字符替换为 RFC 3986 规范(例如,空格变成 %20 而不是“+”),我们只在可能有一些强调的变量:

    sNome_Aluno       := '&nome_aluno='       + fnstUrlEncodeUTF8('Some Student Name');
    sNome_Responsavel := '&nome_responsavel=' + fnstUrlEncodeUTF8('Another Name');
    sDescricao        := '&titulo='           + fnstUrlEncodeUTF8('Just a test');
    

    它奏效了。

    此外,我们删除了 Remy Lebeau 提到的 TIdHTTP 的第二个实例。

    谢谢。

    编辑:我只是 PHP 开发人员,我与另一个开发人员是 Delphi 开发人员一起工作。不幸的是,我们在一家不想收购较新的 Delphi 的小公司工作。

    【讨论】:

      猜你喜欢
      • 2013-11-27
      • 1970-01-01
      • 1970-01-01
      • 2011-12-25
      • 2016-12-20
      • 2018-06-06
      • 1970-01-01
      • 1970-01-01
      • 2016-09-15
      相关资源
      最近更新 更多