POST, отправленный Delphi 5, сломал мой API PHP GraphQL

Я сделал простой API в PHP, чтобы получать переменные из запросов GET / POST и публиковать их в стороннем API-интерфейсе GraphQL. Но у меня проблемы с кодировкой контента.

Приложение работает таким образом:

Приложение Delphi 5 для настольных компьютеров отправляет простой GET или POST с некоторыми переменными в мой PHP API, а мой PHP API отправляет запрос стороннему API GraphQL.

GraphQL, который я отправляю:

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

Пример URL запроса:

HTTP: //api-link/request.php маркер = xyzwsa&ID = 17&типо = Boleto&мат = 123456&nome_aluno = Некоторые% 20Name% 20Here&nome_responsavel = Другой% 20Name&título = Just% 20a% 20Test&Numero = 001122&venc = 2018-04-02&Доблесть = 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? Или оба?

РЕДАКТИРОВАТЬ: пробовал Delphi 5 и Delphi 2006 с Indy v10.1.5, используя HTTP-глаголы GET и POST. Та же ошибка

Спасибо за внимание.

0

Решение

Есть несколько проблем с вашим кодом 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

Другие решения

Мы определили, что эта проблема возникает только со строками с ударением.
Итак, после небольшого исследования мы нашел решение (возможно, не самый элегантный) для замены символов 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');

И это сработало.

Также мы удалили второй экземпляр TIdHTTP упомянутый Реми Лебо.

Благодарю.

РЕДАКТИРОВАТЬ: я только разработчик PHP, и я работаю с другим разработчиком, который является разработчиком Delphi. Мы работаем в небольшой компании, которая, к сожалению, не хочет приобретать более новую Delphi.

0

По вопросам рекламы [email protected]