Перейти к содержанию
  • 0
Bob32

THTTPClient - непонятная ошибка при POST-е json на сервер

Вопросы

Добрый день!

 

пытаюсь вот таким образом json-обьект запостить на сервер (это платежный шлюз).

получаю от сервера в респонзе ответ - 

 

{"Success":false,"ErrorCode":"5","Message":"Неверный запрос.","Details":"Неверный content-type application/x-www-form-urlencoded;charset=UTF-8. Необходимо отправлять запрос с указанием в заголовке application/json."}

"Неверный content-type application/x-www-form-urlencoded;charset=UTF-8. Необходимо отправлять запрос с указанием в заголовке application/json"

почему он неверный???? я же ниже в коде ставлю ContentType именно в application/json…..

 

Что я делаю не так?

 

Код:

 

 FHTTPClient:=THTTPClient.Create;
      FHTTPClient.ConnectionTimeout:=ConstHTTPClientConnectionTimeout;
      FHTTPClient.ResponseTimeout:=ConstHTTPClientResponseTimeout;
      FHTTPClient.UserAgent:='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
      FHTTPClient.Accept:='text/html,application/json,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8';
      FHTTPClient.AcceptEncoding:='gzip, deflate';
      FHTTPClient.AcceptLanguage:='ru,en-US;q=0.8,en;q=0.6';

      FHTTPClient.ContentType:='application/json';
      FHTTPClient.AcceptCharSet := 'UTF-8';

      try

        try
          HTTPResponse:=FHTTPClient.Post('https://securepay.tinkoff.ru/v2/Init', Memo1.Lines);
          if Assigned(HTTPResponse) and (HTTPResponse.StatusCode = 200) then
            begin
            //  if Assigned(fmRating.Image14.Bitmap) then fmRating.Image14.Bitmap.Free;

              Memo2.Lines.LoadFromStream(HTTPResponse.ContentStream);

            end;
        except

        end;
      finally
        if Assigned(FHTTPClient) then FHTTPClient.Free;
      end;

Поделиться сообщением


Ссылка на сообщение

Рекомендуемые сообщения

  • 0

Bytes:=TEncoding.UTF8.GetBytes(Memo1.Lines.Text);
BytesStream:=TBytesStream.Create(Bytes);
try
HTTPResponse:=FHTTPClient.Post('https://securepay.tinkoff.ru/v2/Init', ByteStream);
.....

finally
  BytesStream.Free;
end

Поделиться сообщением


Ссылка на сообщение
  • 0
6 минут назад, Slym сказал:

Bytes:=TEncoding.UTF8.GetBytes(Memo1.Lines.Text);
BytesStream:=TBytesStream.Create(Bytes);
try
HTTPResponse:=FHTTPClient.Post('https://securepay.tinkoff.ru/v2/Init', ByteStream);
.....

finally
  BytesStream.Free;
end

Я не хочу спорить, но по-моему, он ругается в ответе на заголовок, а не на кодировку контента.  Вы уверены в том, что это поможет? 

Поделиться сообщением


Ссылка на сообщение
  • 0
13 минут назад, Slym сказал:

Bytes:=TEncoding.UTF8.GetBytes(Memo1.Lines.Text);
BytesStream:=TBytesStream.Create(Bytes);
try
HTTPResponse:=FHTTPClient.Post('https://securepay.tinkoff.ru/v2/Init', ByteStream);
.....

finally
  BytesStream.Free;
end

и Bytes - какого типа?

Поделиться сообщением


Ссылка на сообщение
  • 0
19 минут назад, Slym сказал:

Bytes:=TEncoding.UTF8.GetBytes(Memo1.Lines.Text);
BytesStream:=TBytesStream.Create(Bytes);
try
HTTPResponse:=FHTTPClient.Post('https://securepay.tinkoff.ru/v2/Init', ByteStream);
.....

finally
  BytesStream.Free;
end

ПРОКАТИЛО - СПАСИБО!! ))

Поделиться сообщением


Ссылка на сообщение
  • 0
2 часа назад, Slym сказал:

Bytes:=TEncoding.UTF8.GetBytes(Memo1.Lines.Text);
BytesStream:=TBytesStream.Create(Bytes);
try
HTTPResponse:=FHTTPClient.Post('https://securepay.tinkoff.ru/v2/Init', ByteStream);
.....

finally
  BytesStream.Free;
end

Хммм. 
все работает, если это ТМемо, в которое я в обджект инспекторе вбил текст запроса - не трогаю ран-тайм. 
 

если ручками в нем ран-тайм меняю с виртуальной клавиатуры значения отправляемых параметров - то получаю от сервера вместо 200 кода - 400-сотый. Кодировка видно сбивается. 

при этом, до постинга на сервер делаю тестовое считывание BytesStream в другое ТМемо - там все отображается верно. Может подскажите, почему? 
у меня ума не хватает понять самостоятельно (

Поделиться сообщением


Ссылка на сообщение
  • 0

и ручками править хрупкую структуру - нунах...
там у тебя json, проверяй валидность json
json:=TJSONObject.ParseJSONValue(Bytes, 0);
 

Поделиться сообщением


Ссылка на сообщение
  • 0
10 часов назад, Slym сказал:

и ручками править хрупкую структуру - нунах...
там у тебя json, проверяй валидность json
json:=TJSONObject.ParseJSONValue(Bytes, 0);
 

проверяю как Вы сказали - валидность json-а ломается после внесения изменений в текст запроса в TMemo.

просто интересно - ПОЧЕМУ???

(если запрос написан в обджект инспекторе - все ок, если ручками рантайм правлю значение параметра - я сейчас программу тестирую - это удобно - вот эта схема с последующей конвертацией ТМемо в Bytes и дальше в пост - ломается......)

Поделиться сообщением


Ссылка на сообщение
  • 0

нормально у меня работают и Get и Post и кодировка нормально принимается.

вам нужно проверить, что же реально уходит на сервер 

для этого есть эхо сервера например https://docs.postman-echo.com

либо направить на любой свой сервер, сделать неск строк скрипт на PHP который покажет все заголовки и контент, который он получил

 

Совершенно непонятно, для чего тут TBytesStream, TStringStream работает абсолютно прозрачно (под капотом ессно то же самое)

Ну и делать общение с сервером в главном потоке - странное занятие конечно

Поделиться сообщением


Ссылка на сообщение
  • 0

Наткнулся сегодня на продолжение этой истории - код, который предложили вчера - работает под иосом, но не работает под андроидом. Даже нетронутый ран-тайм Мемо - при такой конвертации в Bytes и ещё до отправки На сервер в json не распаковывается. Сервер соответственно его тоже не понимает. Ручками такую структуру городить как объект Джейсона рантайм геморроидально  - структура большая. Поэтому я ее сейчас в виде болванки гружу в мемо текстом, и в ходе работы replace-ом меняю в теле параметры - вместо %идентификатор% в болванку подставляю значение. Может подскажите идею - как это можно сделать ещё, чтобы на эти грабли не налетать? Те - тут и до отправки на внешний сервер понятно, что что-то ломается, тк

son:=TJSONObject.ParseJSONValue(Bytes, 0);
... под андроидом даже из девственной болванки нормально считать json не может. 
под иосом этот же код работает.

Поделиться сообщением


Ссылка на сообщение
  • 0

да что это за таинственные структуры, что так сложны? какой размер полученного JSON ?

если бы все было так печально, то форумы бы ломились от одинаковых тем, но ведь не видно такого

хотя родные либы json мне не нравится, я пользуюсь XSuperObject, но они 100% рабочие

проблема 100% в коде

Поделиться сообщением


Ссылка на сообщение
  • 0
11 часов назад, krapotkin сказал:

да что это за таинственные структуры, что так сложны? какой размер полученного JSON ?

если бы все было так печально, то форумы бы ломились от одинаковых тем, но ведь не видно такого

хотя родные либы json мне не нравится, я пользуюсь XSuperObject, но они 100% рабочие

проблема 100% в коде

Добрый день! читаю периодически Ваши сообщения, знаю Вас, как человека  очень грамотного, поэтому слова эти меня зацепили.... )))

и сделал я вот что:

встал в 5 утра ж))), залез на свой сервер, на php написал небольшую заглушку ловить такие POST-ы.

Запустил Дельфи. Теперь в сухом остатке:

 

(в Мемо1.Lines - json структура)

 

Код:

HTTPResponse:=FHTTPClient.Post('https://umka.space/api/index.php'{'https://securepay.tinkoff.ru/v2/Init'}, Memo1.Lines);

НЕ РАБОТАЕТ ни на Иосе, ни на Андроиде - на сервер вообще никто по посту не стучится...

хотя вот как выглядит в хелпе описание этой функции - передавать TStrings - можно!

function Post(const AURL: string; const ASourceFile: string; const AResponseContent: TStream = nil; const AHeaders: TNetHeaders = nil): IHTTPResponse; overload;
function Post(const AURL: string; const ASource: TStrings; const AResponseContent: TStream = nil;  const AEncoding: TEncoding = nil; const AHeaders: TNetHeaders = nil): IHTTPResponse; overload;
function Post(const AURL: string; const ASource: TStream; const AResponseContent: TStream = nil;  const AHeaders: TNetHeaders = nil): IHTTPResponse; overload;
function Post(const AURL: string; const ASource: TMultipartFormData; const AResponseContent: TStream = nil;  const AHeaders: TNetHeaders = nil): IHTTPResponse; overload;

 

 

Код:

BytesStream:=TBytesStream.Create(TEncoding.UTF8.GetBytes(Memo1.Lines.Text));

HTTPResponse:=FHTTPClient.Post('https://umka.space/api/index.php'{'https://securepay.tinkoff.ru/v2/Init'}, BytesStream);

работает и на иосе и на андроиде - в логе сервера в обоих случаях появяется моя структура - в том виде в котором надо.

вывод функция Post, которая получает TStrings - хреновая.

 

но это пол беды - эта проблема как раз решена! теперь вторая часть - то что не решено -

когда POST сгружает ответный json, котрый прислали в ответ на мой запрос в HTTPResponse.ContentStream

(это продолжение этого же кода)

if Assigned(HTTPResponse) and (HTTPResponse.StatusCode = 200) then
        begin
           Memo2.Lines.LoadFromStream(HTTPResponse.ContentStream)

           JSON:=TJSONObject(TJSONObject.ParseJSONValue(Memo2.Lines.Text))
        end

 

 

то на иосе в Мемо2 сгружается хороший json и он замечательно потом распарсивается в следующей строчке.

а вот под андроидом в ContentStream образуется какой-то мусор с периодически встречающимися словами "TMemoryStream". попыка его даже загрузить в Мемо часто приводит к эксепшену "No mapping for the Unicode character exists in target multi-bytecode page".

под иосом и андроидом это происходит на одном и том же коде - при одинаковых вводных параметрах. верьте мне! )

 

ВЫВОД - или функция битая, или json ломается при заливании в TMemo.

Дайте пожалуйста совет - как это можно быстро исправить. полтора дня потерял. (

другие компоненты? есть что-то бесплатное на примете? в инди вроде есть свой аналогичный httpclient….. но я им никогда не пользовался. 

все это происходит под 10.3.3 Rio Community Edition.

 

СПАСИБО! )

Поделиться сообщением


Ссылка на сообщение
  • 0

...или как это можно из HTTPResponse.ContentStream скгружать ParseJSONValue для парсинга минуя ТМемо?

там вот такие типы допускаются:

class function ParseJSONValue(const Data: PByte; const Offset: Integer; const ALength: Integer; Options: TJSONParseOptions): TJSONValue; overload; static;
class function ParseJSONValue(const Data: TArray<Byte>; const Offset: Integer; IsUTF8: Boolean = True): TJSONValue; overload; inline; static;
class function ParseJSONValue(const Data: TArray<Byte>; const Offset: Integer; Options: TJSONParseOptions): TJSONValue; overload; inline; static;
class function ParseJSONValue(const Data: TArray<Byte>; const Offset: Integer; const ALength: Integer; IsUTF8: Boolean = True): TJSONValue; overload; inline; static;
class function ParseJSONValue(const Data: TArray<Byte>; const Offset: Integer; const ALength: Integer; Options: TJSONParseOptions): TJSONValue; overload; inline; static;
class function ParseJSONValue(const Data: string; UseBool: Boolean = False; RaiseExc: Boolean = False): TJSONValue; overload; static;
class function ParseJSONValue(const Data: UTF8String; UseBool: Boolean = False; RaiseExc: Boolean = False): TJSONValue; overload; static

 

возможно имеет смысл как-то байтами это передать, по аналогии, как туда запрос формировали. я не очень силен в этих новомодных штучках, - как это можно сделать альтернативным образом?

Поделиться сообщением


Ссылка на сообщение
  • 0

А покажите заголовки, которые приходят вместе с вашим запросом на сервер ?

Судя по ошибке, сервер отвечает вам не в кодировке UTF8.

За это отвечает AcceptCharset

Отредактировал krapotkin

Поделиться сообщением


Ссылка на сообщение
  • 0

вот мой рабочий код, который я использую в нескольких проектах

https://bitbucket.org/vkrapotkin/commonapi/src/master/

он конечно посложнее, но идея та же

заголовки, StringStream на отправку и на прием данных

Разбор полученного текста в json если это json

Поделиться сообщением


Ссылка на сообщение
  • 0
2 минуты назад, krapotkin сказал:

А покажите заголовки, которые приходят вместе с вашим запросом на сервер ?

Судя по ошибке, сервер отвечает вам не в кодировке UTF8.

За это отвечает AcceptEncoding

httpclient я настраиваю так:  

FHTTPClient:=THTTPClient.Create;
  FHTTPClient.ConnectionTimeout:=ConstHTTPClientConnectionTimeout;
  FHTTPClient.ResponseTimeout:=ConstHTTPClientResponseTimeout;
  FHTTPClient.UserAgent:='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
  FHTTPClient.Accept:='text/html,application/json,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8';
  FHTTPClient.AcceptEncoding:='gzip, deflate';
  FHTTPClient.AcceptLanguage:='ru,en-US;q=0.8,en;q=0.6';
  FHTTPClient.ContentType:='application/json';
  FHTTPClient.AcceptCharSet := 'UTF-8';

а как смотреть на сервере эти заголовки? я в php тоже не силён. (

Поделиться сообщением


Ссылка на сообщение
  • 0

упс, опечатался, AcceptCharset конечно

но тут есть нюансы

поэтому я спрашиваю именно что реально приходит на сервер а не что настроено при отправке

иногда это не одно и то же. я тоже долго разбирался с этой проблемой и там много всяких нюансов бывает.

В последний раз убил два дня, чтобы понять, что реальный сервер делает redirect а по стандарту Delphi делает POST редирект как GET. И это все портило.

Пришлось подкрутить немного...

Поделиться сообщением


Ссылка на сообщение
  • 0
1 минуту назад, krapotkin сказал:

кстати, требуется же ответ сервера JSON ? тогда

FHTTPClient.Accept:='application/json'

а если он там среди прочих перечислен  - могут быть проблемы?

(сейчас заголовки подкручу)

 

Спасибо Вам за помощь!

Поделиться сообщением


Ссылка на сообщение
  • 0

это зависит от того, кто программировал этот сервер)
но вы же ему по сути передаете "отвечай как хочешь". и первый в списке html

 

 

Поделиться сообщением


Ссылка на сообщение
  • 0
10 минут назад, krapotkin сказал:

упс, опечатался, AcceptCharset конечно

но тут есть нюансы

поэтому я спрашиваю именно что реально приходит на сервер а не что настроено при отправке

иногда это не одно и то же. я тоже долго разбирался с этой проблемой и там много всяких нюансов бывает.

В последний раз убил два дня, чтобы понять, что реальный сервер делает redirect а по стандарту Delphi делает POST редирект как GET. И это все портило.

Пришлось подкрутить немного...

    [Host] => umka.space
    [Port] => 443
    [X-Forwarded-For] => 31.173.84.116
    [X-Real-IP] => 31.173.84.116
    [X-Forwarded-Port] => 443
    [X-MH-Host] => umka.space
    [Content-Length] => 760
    [Accept-Language] => ru,en-US;q=0.8,en;q=0.6
    [Accept-Encoding] => gzip, deflate
    [Accept] => application/json
    [Content-Type] => application/json
    [User-Agent] => Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586
    [Accept-Charset] => UTF-8
FHTTPClient.Accept:='application/json' оставил только это - выше заголовки уже то, что получается после после этого изменения)

Поделиться сообщением


Ссылка на сообщение
  • 0

приложите мин пример, или если приватные данные, в личку,  могу запустить на Android

Поделиться сообщением


Ссылка на сообщение
  • 0
1 минуту назад, krapotkin сказал:

приложите мин пример, или если приватные данные, в личку,  могу запустить на Android

а что имеется ввиду - форма с кодом?

весь проект очень большой. или просто кусок кода?

Поделиться сообщением


Ссылка на сообщение
  • 0

не работает же конкретный кусок

его можно вынести в отдельный проект или просто кинуть кусок кода, там же один код, компоненты не требуются. нужен только URL и все собсно

Поделиться сообщением


Ссылка на сообщение
  • 0
4 минуты назад, krapotkin сказал:

не работает же конкретный кусок

его можно вынести в отдельный проект или просто кинуть кусок кода, там же один код, компоненты не требуются. нужен только URL и все собсно

  Str:=Memo1.Lines.Text;
  Str:=Str.Replace('%Amount%',IntToStr(StrToInt(Edit4.Text)*100),[rfReplaceAll]);
  Str:=Str.Replace('%OrderId%',IntToStr(OrderId));
  Str:=Str.Replace('%Email%',fmSettings.Edit5.Text,[rfReplaceAll]);
  Str:=Str.Replace('%Phone%',fmSettings.Edit4.Text,[rfReplaceAll]);


  BytesStream:=TBytesStream.Create(TEncoding.UTF8.GetBytes(Str));

  FHTTPClient:=THTTPClient.Create;
  FHTTPClient.ConnectionTimeout:=ConstHTTPClientConnectionTimeout;
  FHTTPClient.ResponseTimeout:=ConstHTTPClientResponseTimeout;
  FHTTPClient.UserAgent:='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2486.0 Safari/537.36 Edge/13.10586';
  FHTTPClient.Accept:='application/json';
  FHTTPClient.AcceptEncoding:='gzip, deflate';
  FHTTPClient.AcceptLanguage:='ru,en-US;q=0.8,en;q=0.6';
  FHTTPClient.ContentType:='application/json';
  FHTTPClient.AcceptCharSet := 'UTF-8';

    try
    try
      HTTPResponse:=FHTTPClient.Post({'https://umka.space/api/index.php'}'https://securepay.tinkoff.ru/v2/Init', BytesStream);
      if Assigned(HTTPResponse) and (HTTPResponse.StatusCode = 200) then
        begin
          if Assigned(HTTPResponse.ContentStream) then Memo2.Lines.LoadFromStream(HTTPResponse.ContentStream)
          else Memo2.Lines.Clear;
        end
      else
        begin
          lbError.Text:='Connection problems... #380 '+IntToStr(HTTPResponse.StatusCode);
          AniIndicator1.Visible:=False;
          AniIndicator1.Enabled:=False;
          exit;
        end;

    except
      Memo2.Lines.Clear;
    end;
  finally
    if Assigned(FHTTPClient) then FHTTPClient.Free;
    BytesStream.Free;
  end;

  //Memo2.Lines.LoadFromStream(HTTPResponse.ContentStream);

  JSON:=TJSONObject(TJSONObject.ParseJSONValue(Memo2.Lines.Text));

  if not JSON.TryGetValue('Success', Success) then
    begin
      lbError.Text:='Connection problems... #382';
      AniIndicator1.Visible:=False;
      AniIndicator1.Enabled:=False;
      exit;
    end;

Поделиться сообщением


Ссылка на сообщение

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.


  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу

×
×
  • Создать...