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