Delphi-PRAXiS
Seite 3 von 4     123 4      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Indy HTTPClient Post umlaute (https://www.delphipraxis.net/214978-indy-httpclient-post-umlaute.html)

DelTurbo 17. Apr 2024 10:30

AW: Indy HTTPClient Post umlaute
 
Zitat:

Zitat von Michael II (Beitrag 1535830)
"{ \"q\": \"L\u00e4nge 8,5 cm\",\"source\": \"de\",

Das nimmt er nicht an. Er will das wirklich so wie ich geschrieben hatte.

Hier der Code, also der Kern. Alles zu posten ist was lang.
Indy:
Delphi-Quellcode:
    pdata:=TStringList.Create;
    for i:=0 to memoPost.Lines.Count-1 do begin
      pdata.Add(UTF8Encode(memoPost.Lines.Strings[i]));
    end;
    try
      line:=HTTPClient.Post(URL,pdata);
    except
      i:=1;
    end;
    memoAnswer.Lines.Text:=line;
ICS:
Delphi-Quellcode:
    SndStrm := TMemoryStream.Create;
    RcvStrm := TMemoryStream.Create;
    Data := UTF8Encode(memoPost.Lines.Text);
    HttpCli1.SendStream := SndStrm;
    HttpCli1.SendStream.Write(Data[1],Length(Data));
    HttpCli1.SendStream.Seek(0,0);
    HttpCli1.RcvdStream := RcvStrm;
    HttpCli1.URL := URL;
    HttpCli1.Post;
    RcvStrm.Position:=0;
    memoAnswer.Lines.LoadFromStream(RcvStrm);
    memoAnswer.Lines.Add('....');
    RcvStrm.Free;
    SndStrm.Free;
ICS ging auf anhieb. Und wie gesagt ich denke das ich die gebuggte Version von Indy habe. Ich habe vor Jahren mal Indy 10.6.0 Installiert. Nun wo man es weiß, sollte ich das mal updaten. Man kann ja auch mit Wireshark sehen das Indy das falsch übergibt. Indy macht ja die ? rein.

jaenicke 17. Apr 2024 13:29

AW: Indy HTTPClient Post umlaute
 
Das stimmt, mit dem UTF8Encode kommt es falsch an. Es sei denn ich lese es auf dem Server auch wieder als UTF-8 ein:
Delphi-Quellcode:
<?php
print_r(mb_convert_encoding($_POST, 'ISO-8859-1', 'UTF-8'));
?>
Der Grund ist simpel:
Du kodierst es zwar als UTF-8, teilst das aber nicht mit. ICS wiederum verwendet UTF-8 Encoding als Standard, weshalb du das dort nicht explizit setzen musst.

Korrekt mit Indy ist daher:
Delphi-Quellcode:
    pdata:=TStringList.Create;
    for i:=0 to memoPost.Lines.Count-1 do begin
      pdata.Add(UTF8Encode(memoPost.Lines.Strings[i]));
    end;
    try
      HTTPClient.Request.ContentEncoding := 'UTF-8';
      line:=HTTPClient.Post(URL,pdata);
    except
      i:=1;
    end;
    memoAnswer.Lines.Text:=line;

DelTurbo 17. Apr 2024 14:18

AW: Indy HTTPClient Post umlaute
 
Hi, bitte steck da keine Zeit mehr rein.

Delphi-Quellcode:
HTTPClient.Request.ContentEncoding := 'UTF-8';


Das habe ich alles versucht. Bevor ich hier Frage, kannst du davon ausgehen das ich mind. 2 Tage versuche das selber zu lösen. Und ja, das php gibt UTF-8 zurück und nimmt es an.

Ich danke dir trotzdem Tausend mal für deine Hilfe und mühe. :thumb:

Michael II 17. Apr 2024 18:05

AW: Indy HTTPClient Post umlaute
 
Delphi-Quellcode:
procedure libretranslate_Indy;
var
    json : rawbytestring;
    line : WideString;
    Stream: TStream;
begin
    Stream := TMemoryStream.Create;
   try
    json := UTF8Encode('{ "q": "anöa","source": "auto", "target": "en", "api_key": ""}');
    Stream.WriteBuffer(Pointer(json)^, Length(json));
    Stream.Position := 0;
    form103.idhttp1.Request.ContentType := 'application/json';
    line := UTF8Decode(form103.idhttp1.Post('http://127.0.0.1:5000/translate',stream));
  // line:=form103.idhttp1.Post('http://httpbin.org/anything',pdata);
    Showmessage( line );
   finally
    stream.Free;
   end;
end;
jaenicke ich habe libretranslate lokal installiert (und Fiddler fürs Vergleichen von Header und Daten und...) und da führt dein Code beim "Post" zu einem Bad Request oder einem Method not allowed. Eventuell mache ich was falsch. - Ich glaube aber, dass der Code oben funktionieren sollte. Bei mir werden die Sonderzeichen im Response als Sonderzeichen angezeigt.

Sogar das funktioniert: json := UTF8Encode('{ "q": "أرى الكلب الأزرق الصغير خلف المنزل الأصفر على النهر الأحمر.","source": "auto", "target": "en", "api_key": ""}');

DelTurbo 18. Apr 2024 10:23

AW: Indy HTTPClient Post umlaute
 
Hi,
du musst auf jeden fall hoForceEncodeParams auf False setzen.

Nachtrag: Nimm doch ICS, klappt auf anhieb. Ich habe grade mal Indy 10.6.3.1 Installiert. Selber Fehler.

Michael II 18. Apr 2024 16:23

AW: Indy HTTPClient Post umlaute
 
Der Code in #24 funktioniert.

Ich nutze Indy sonst nicht - nur weil du gefragt hast, habe ich das Ding wieder einmal angeschaut - und es reicht wieder für ein paar Wochen. Ich nutze ICS. Fürs Posten setze ich wohl häufiger TNetHTTPClient, weil diese Komponente direkt mit den Sicherheitsfunktionen (https Unterstützung ohne "Basteln") von Windows interagiert.

Herzlichen Dank für deinen Supertipp "libretranslate" - kannte ich nicht, ist grossartig und es fallen mir viele Dinge ein, welche ich tun würde, wenn ich mehr Zeit hätte...

Dein Tipp "hoForceEncodeParams entfernen" gilt fürs Post mit zweitem Parameter TStringList, da sonst die Daten nur im HTTP Header landen und von libretranslate nicht "gesehen" werden (Bad request). Für post via Stream spielt die Option keine Rolle. Stream wird (wie von libretranslate gewünscht) im Body übertragen.

Wenn ich den Indy Code anschaue, glaube ich, dass das Posten in diesem Fall mit TStrings nicht klappen kann. Wünsche wie Request.CharSet := 'utf-8'; Request.ContentEncoding := 'utf-8' oder utf8 beim Post als dritten Parameter setzen werden von Indy nicht berücksichtigt. Indy behandelt die von dir beim Post als zweiten Parameter übergebene TStringList als Parameter vom Typ par1=val1&par2=val2.... und codiert diese entsprechend via URLEncode. Die ?, welche du gesehen hast waren "nicht druckbare" Zeichen; HEX Monitor nutzen wäre hier u.U. aufschlussreicher. Fiddler ist übrigens grossartig, wenn du http Traffic untersuchen willst.

Kurz: Beim Posten der json Daten statt TStrings einen Stream verwenden löst das Problem.

Ich habe in #24 TMemorystream verwendet. Viel schöner ist es mit einem TStringStream (Copilot hat dies übrigens vorgeschlagen, als ich fragte, ob man den Code vereinfachen oder schöner schreiben könne.) Ich habe nun auch eine Fehlerbehandlung (ich habe Copilot nicht gefragt was KI davon hält ;-)) eingebaut:


Delphi-Quellcode:
function fehler : string;
var fehlermeldung : PCHar;
begin
 getmem( fehlermeldung , 600 );
 ExceptionErrorMessage( ExceptObject, ExceptAddr, fehlermeldung, 599 );
 Result := fehlermeldung;
end;

function libretranslate_Indy(uebersetze : string; var uebersetzung : string ) : boolean;
// RESULT=false, wenn eine Exception aufgetreten ist
// Bei RESULT=false wird in uebersetzung die Fehlermeldung übergeben
// Bei RESULT=true wird in übersetzung die Serverantwort von libretranslate zurückgeben.
var
    JsonToSend : TStringStream;
    idhttp : TIdHTTP;

begin
     Result := false;
  try
     JsonToSend := TStringStream.Create(uebersetze, TEncoding.UTF8);
     idhttp := TIdHTTP.Create(nil);
   try
     idhttp.Request.ContentType := 'application/json';
     uebersetzung := UTF8Decode(idhttp.post('http://127.0.0.1:5000/translate',jsontosend));
     Result := true;
   finally
    jsontosend.free;
    idhttp.Free;
   end;
  except
    uebersetzung := fehler;
    Result := false;
  end;
end;
oder via TNetHTTPClient (SSL Unterstützung)
Delphi-Quellcode:
procedure WriteStringToStream(Stream: TStream; const Str: String);
var
  Data: TBytes;
begin
  Data := TEncoding.UTF8.GetBytes(Str);
  Stream.WriteBuffer(Data[0], Length(Data));
end;

function CreateHTTPClient: TNetHTTPClient;
begin
  Result := TNetHTTPClient.Create(nil);
end;

function CreateHTTPRequest(Client: TNetHTTPClient): TNetHTTPRequest;
begin
  Result := TNetHTTPRequest.Create(nil);
  Result.Client := Client;
  Result.CustomHeaders['Content-Type'] := 'application/json';
end;

function CreateParamsStream(const Str: String): TStream;
begin
  Result := TMemoryStream.Create;
  WriteStringToStream(Result, Str);
  Result.Position := 0;
end;
(* SSL/TLS-Details abstrahiert; interagiert direkt mit den Windows-Sicherheitsfunktionen *)
procedure Uebersetze;
var
  HTTPClient: TNetHTTPClient;
  HTTPRequest: TNetHTTPRequest;
  Params: TStream;
  Response: IHTTPResponse;
begin
  HTTPClient := CreateHTTPClient;
  HTTPRequest := CreateHTTPRequest(HTTPClient);
  Params := CreateParamsStream(atest);
  try
    Response := HTTPRequest.Post('http://127.0.0.1:5000/translate', Params);
    ShowMessage(Response.ContentAsString(TEncoding.UTF8));
  finally
    HTTPRequest.Free;
    HTTPClient.Free;
    Params.free;
  end;
end;

DelTurbo 18. Apr 2024 16:50

AW: Indy HTTPClient Post umlaute
 
Hi,
sowas sollte man sich mit Wireshark ansehen.

Hier mal kurz.


Indy mit hoForceEncodeParams
Code:
POST /translate HTTP/1.0
Connection: keep-alive
Content-Encoding: UTF-8
Content-Type: application/json
Content-Length: 167
Host: translate.lan:5000
Accept: application/json
Accept-Charset: UTF-8
Accept-Encoding: UTF-8, identity
User-Agent: 'User-Agent', 'Mozilla/5.0

%7B+%22q%22%3A+%22L%C3%83%C2%A4nge+8%2C5+cm%22%2C%22source%22%3A+%22de%22%2C+%22target%22%3A+%22en%22%2C+%22api_key%22%3A+%22%22%7D
Das würde normalerweise wieder zusammen gebastelt. Aber Libre kann bzw. macht das nicht.

Indy ohne hoForceEncodeParams
Code:
POST /translate HTTP/1.0
Connection: keep-alive
Content-Encoding: UTF-8
Content-Type: application/json
Content-Length: 105
Host: translate.lan:5000
Accept: application/json
Accept-Charset: UTF-8
Accept-Encoding: UTF-8, identity
User-Agent: 'User-Agent', 'Mozilla/5.0

{ "q": "L??nge 8,5 cm","source": "de", "target": "en", "api_key": "##########"}
ICS:
Code:
POST /translate HTTP/1.0
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
Content-Type: application/json
User-Agent: Mozilla/4.0
Host: translate.lan:5000
Content-Length: 107

{ "q": "Länge 8,5 cm","source": "de", "target": "en", "api_key": "############"}

Michael II 18. Apr 2024 17:17

AW: Indy HTTPClient Post umlaute
 
Ja ich weiss. Libre ist nicht so frei im Denken. Aber meinen Code hast du probiert? Sollte mit dem aktuellen libretranslate klappen. Und mit Indy TStrings geht's ja vielleicht auch nach Setzen von Eigenschaft X und Abschalten von Y und Löschen von Z ;-).

DelTurbo 18. Apr 2024 17:25

AW: Indy HTTPClient Post umlaute
 
Zitat:

Zitat von Michael II (Beitrag 1535909)
Ja ich weiss. Libre ist nicht so frei im Denken. Aber meinen Code hast du probiert? Sollte mit dem aktuellen libretranslate klappen. Und mit Indy TStrings geht's ja vielleicht auch nach Setzen von Eigenschaft X und Abschalten von Y und Löschen von Z ;-).

Hi,
das habe ich schon alles hinter mir.

jaenicke 21. Apr 2024 02:30

AW: Indy HTTPClient Post umlaute
 
Nun habe ich doch kurz libretranslate in einer meiner Linux-VMs installiert. Es stimmt, die Variante mit TStrings funktioniert nicht so wie erwartet. Das bekommt man zwar hin, aber einfacher ist es so:
Delphi-Quellcode:
function Translate(const AText, ASourceLang, ADestLang, AApiKey: string): string;
var
  Http: TIdHTTP;
  Data: TStream;
begin
  Http := TIdHTTP.Create(nil);
  try
    Data := TStringStream.Create(UTF8Encode(
      Format('{"q": "%s","source": "%s", "target": "%s", "api_key": "%s"}', [AText, ASourceLang, ADestLang, AApiKey])));
    try
      Http.Request.ContentType := 'application/json';
      Result := UTF8Decode(Http.Post('http://192.168.119.53:5000/translate', Data));
    finally
      Data.Free;
    end;
  finally
    Http.Free;
  end;
end;

  ShowMessage(Translate('Länge 8,5 cm', 'de', 'en', ''));
  ShowMessage(Translate('Length 8,5 cm', 'en', 'de', ''));
Das funktioniert unter Delphi 2007 und z.B. Delphi 11 analog. Genauso klappt es auch mit ICS. Manuell in den Stream schreiben ist auch dort nicht notwendig.

Wenn du Streams verwendest, funktioniert es mit Indy und ICS identisch.


Alle Zeitangaben in WEZ +1. Es ist jetzt 22:21 Uhr.
Seite 3 von 4     123 4      

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz